Preparations

Loading the neccessary packages

library("gridExtra")

Attaching package: ‘gridExtra’

The following object is masked from ‘package:Biobase’:

    combine

The following object is masked from ‘package:BiocGenerics’:

    combine

The following object is masked from ‘package:dplyr’:

    combine
save_csv <- TRUE

Loading the data

seurat_10X2 <- readRDS(file = "seurat_10X2_clustered_min_1500_nGene.RDS")
TSNEPlot(seurat_10X2 , do.label = TRUE)

Set the identity to celltype and age combination

seurat_10X2@meta.data[,"celltype_age"] <- paste( seurat_10X2@meta.data$celltype , seurat_10X2@meta.data$age , sep = "_" )

Differentially expressed genes between cells from old and young mice

DESeq2

Differential Expression between NSCs from old vs young animals with DESeq2

Differential Expression between qNSCs for old vs young animals and aNSC for old vs young animals with DESeq2

Differential Expression between young and old for all celltypes individually

seurat_10X2 <- SetAllIdent( object = seurat_10X2 , id = "celltype_age" )
TSNEPlot(object = seurat_10X2 , do.label = TRUE )

Differential expression results are always compared old over young meaning that if in the results there is a log2FC of 1, the gene is log2(1) fold higher in the old than in the young

Use DESeq2 for Differential Expression testing of the individual subpopulations

## quiescent neuronal stem cells
 markers_qNSC1_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "qNSC1_old" , ident.2 = "qNSC1_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE  , min.pct = 0 , logfc.threshold = 0)
## quiescent neuronal stem cells (primed)
 markers_qNSC2_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "qNSC2_old" , ident.2 = "qNSC2_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)
## active neuronal stem cells
 markers_aNSC0_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "aNSC0_old" , ident.2 = "aNSC0_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)
 markers_aNSC1_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "aNSC1_old" , ident.2 = "aNSC1_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)
 markers_aNSC2_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "aNSC2_old" , ident.2 = "aNSC2_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)
## TAPs and NB
markers_TAP_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "TAP_old" , ident.2 = "TAP_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)
 markers_NB_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "NB_old" , ident.2 = "NB_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)

# OD and OPC
markers_OPC_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "OPC_old" , ident.2 = "OPC_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)
markers_OD_old_vs_young_DESeq2 <- FindMarkers(object = seurat_10X2 , ident.1 = "OD_old" , ident.2 = "OD_young" , test.use = "DESeq2" , parallel = TRUE , print.bar = FALSE , min.pct = 0 , logfc.threshold = 0)

if(save_csv){
  
 DE_data_list <- list( "genes_qNSC1_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_qNSC1_old_vs_young_DESeq2 , "genes_qNSC2_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_qNSC2_old_vs_young_DESeq2 , "genes_aNSC0_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_aNSC0_old_vs_young_DESeq2 , "genes_aNSC1_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_aNSC1_old_vs_young_DESeq2 , "genes_aNSC2_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_aNSC2_old_vs_young_DESeq2, "genes_TAP_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_TAP_old_vs_young_DESeq2 , "genes_NB_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_NB_old_vs_young_DESeq2 , "genes_OPC_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_OPC_old_vs_young_DESeq2 , "genes_OD_log2FC_old_vs_young_10X_2_DESeq2_from_Seurat" = markers_OD_old_vs_young_DESeq2 )
  
 DE_data_list <- lapply( DE_data_list, FUN = function(x){
   x %>% tibble::rownames_to_column("gene_symbol") %>% mutate( avg_logFC = avg_logFC/log(2) ) %>% dplyr::rename( avg_log2FC = avg_logFC )
 })
 
 if(save_csv){
   for( ct in seq(1,length(DE_data_list)) ){
      write.csv(x = DE_data_list[[ct]] , file = paste0( "results/DE_DESeq2_old_vs_young/celltypes_individual_old_vs_young/DE_" , names(DE_data_list[ct]) , ".csv" ) )  
   }
 }
}

t-test

library(genefilter)

Attaching package: ‘genefilter’

The following objects are masked from ‘package:matrixStats’:

    rowSds, rowVars

The following object is masked from ‘package:readr’:

    spec
tenx_data <- seurat_10X2@data
TPM_NSC <- as.matrix(tenx_data)
# Load the cell annotation
tenx_annotation <- FetchData(object = seurat_10X2 , vars.all = c("celltype","age") ) %>% rownames_to_column("cell") %>% dplyr::rename(type = celltype)
tenx_annotation$type <- factor(x = tenx_annotation$type , levels = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB","OPC","OD"))
tenx_annotation$age <- factor(x = tenx_annotation$age , levels = c("young","old"))
NSC_anno <- tenx_annotation
# Subset the expression matrix for young and old into different matrices
TPM_old <- TPM_NSC[,as.character(NSC_anno[NSC_anno$age == "old",]$cell)]
TPM_young <- TPM_NSC[,as.character(NSC_anno[NSC_anno$age == "young",]$cell)]
NSC_anno_young <- NSC_anno[NSC_anno$age == "young",]
NSC_anno_old <- NSC_anno[NSC_anno$age == "old",]
# Expression values are already log transformed

Define function to run t-test

run_ttest_and_calculate_Cohens_d <- function(subpop){
  
    ## Subset cells for individual subpopulations
    TPM_young_subpop <- TPM_young[,as.character(NSC_anno_young[NSC_anno_young$type == subpop,]$cell)]
    TPM_old_subpop <- TPM_old[,as.character(NSC_anno_old[NSC_anno_old$type == subpop,]$cell)]
    TPM_NSC_subpop <- TPM_NSC[,as.character(NSC_anno[NSC_anno$type == subpop,]$cell)]
  
    # Calculate the mean expression, standard deviation and number of cells with 0 counts for each row (gene) for young and old cells separately
    table_10X <- data.frame( 
      gene_id = rownames(TPM_young_subpop),
      avg_logTPM_young = apply(TPM_young_subpop , MARGIN = 1 , FUN = mean)  , 
      avg_logTPM_old = apply(TPM_old_subpop , MARGIN = 1 , FUN = mean)  ,
      sd_logTPM_young = apply(TPM_young_subpop , MARGIN = 1 , FUN = sd)  ,
      sd_logTPM_old = apply(TPM_old_subpop , MARGIN = 1 , FUN = sd),
      percent_cells_zero_young = apply(TPM_young_subpop == 0 , MARGIN = 1 , FUN = sum)/ncol(TPM_young_subpop),
      percent_cells_zero_old = apply(TPM_old_subpop == 0 , MARGIN = 1 , FUN = sum)/ncol(TPM_old_subpop)
    )
    
    # Calculate the difference between the mean from old and young (old - young)
    table_10X$difference_of_avg <- (table_10X$avg_logTPM_old - table_10X$avg_logTPM_young )
    
    # Calculate the mean Standard Deviation between young and old
    table_10X$mean_sd <- apply( X =  table_10X[,c("sd_logTPM_young","sd_logTPM_old")] , MARGIN = 1 , FUN = mean)
    
    # Finally calculate Cohens D, by deviding the difference of the mean values by the mean standard deviation 
    table_10X$'difference_of_avg/mean_sd' <- table_10X$difference_of_avg/table_10X$mean_sd
    # Add p-values from t-test
    
    # Run t-test on 10X NSC data old and young
    
    # Prepare factor with young and old in order of the column names
    fact <- as.character( colnames(TPM_NSC_subpop) )
    fact[ grepl(x = fact , pattern = "-1") ] <- "old"
    fact[ grepl(x = fact , pattern = "-2" ) ] <- "young"
    fact <- factor( fact )
    
    # Prepare matrix for t-test input
    TPM_NSC_mat <- TPM_NSC_subpop
    TPM_NSC_mat <- as.matrix( TPM_NSC_mat ) 
    
    # Run t-test with rowttests function from genefilter packages
    t_test_NSCs_10X <- rowttests(x = TPM_NSC_mat , fac = fact )
    
    # Add ensembl_gene_id as column from the rownames
    t_test_NSCs_10X$gene_id <- rownames(t_test_NSCs_10X) 
    
    # Join the tables
    table_10X_merged_with_ttest <- full_join(x = table_10X , y = t_test_NSCs_10X , by = "gene_id" )
    table_10X_merged_with_ttest
}

Run the t-test for all subpopulations individually

Compare pseudotime to Seurat analysis

pseudotime_ordering <- read.csv("Monocle/Monocle_pseudotime_assigment.csv" , row.names = 1)
seurat_10X2 <- AddMetaData(object = seurat_10X2 , metadata = pseudotime_ordering["Pseudotime"] )

t-SNE plots with pseudotime

seurat_10X2 <- SetAllIdent( object = seurat_10X2 , id = "celltype" )
TSNEPlot(seurat_10X2)

Overlay pseudotime as color onto the t-SNE plot

We fetch the t-SNE coordinates and the Pseudotime assignment values and plot the t-SNE plot with pseudotime overlayed.

df_plot <- FetchData(object = seurat_10X2, vars.all = c("tSNE_1","tSNE_2","age","Pseudotime","celltype") ) %>% 
            rownames_to_column("cellbarcode") %>%
            filter(! is.na(Pseudotime))
g.eq <- ggplot(
      data = df_plot,
      mapping = aes(
        x = tSNE_1,
        y = tSNE_2,
        color = Pseudotime
        )
     ) +
  geom_point() +
  scale_color_viridis(option = "D") +
  coord_equal()
g.eq

Monocle trajectory plot with colored cell identities

pseudotime_ordering_ident <- data.frame( Cell_Type_Seurat = seurat_10X2@ident) %>% 
                  rownames_to_column(var = "cellbarcode") %>% 
                  full_join( pseudotime_ordering %>% 
                              rownames_to_column(var = "cellbarcode") 
                    , by = "cellbarcode" ) %>% 
                  filter(! Cell_Type_Seurat %in% c("OPC","OD",NA) ) %>% 
                  filter(! is.na(Pseudotime))
p2_ident.split.h_pch21 <- ggplot(pseudotime_ordering_ident, aes(x = Dim1, y = Dim2, fill = factor( Cell_Type_Seurat , levels = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB")) ), alpha = 1) + 
  geom_point(size = 3 , alpha = 1 , color = "black" , pch = 21 ) +
  # theme(text = element_text(size = 16)) + 
  ggtitle('Trajectory of NSC Lineage as arranged by Monocle') + 
  scale_fill_manual(values = c( qNSC1 = "steelblue" , qNSC2 = "steelblue1" , aNSC0 = "tomato" , aNSC1 = "sienna1", aNSC2 = "sienna3" , TAP = "green" , NB = "yellow" ) , name = "Activation state") +
  # theme(panel.background = element_blank() ) +
  # theme_classic() +
  coord_equal() +
  facet_grid(.~Age) 
p2_ident.split.h_pch21

Plot density along pseudotime for old and young

g <- ggplot(data = pseudotime_ordering_ident , aes(x = Pseudotime , color = Cell_Type_Seurat , fill = Cell_Type_Seurat )) + geom_density(bw=2, alpha = 0.7) + scale_fill_manual(values =   c( qNSC1 = "steelblue" , qNSC2 = "steelblue1" , aNSC0 = "tomato" , aNSC1 = "sienna1", aNSC2 = "sienna3" , TAP = "green" , NB = "yellow") ) + scale_color_manual(values =   c( qNSC1 = "steelblue" , qNSC2 = "steelblue1" , aNSC0 = "tomato" , aNSC1 = "sienna1", aNSC2 = "sienna3" , TAP = "green" , NB = "yellow") ) 
g

g <- ggplot(data = pseudotime_ordering_ident , aes(x = Pseudotime , color = Age , fill = Age) ) + geom_density(bw=2, alpha = 0.1)
g

GO Analysis of differentially expressed genes in celltypes

Define function to run GO and GSEA analysis and make a dotplot from the results

GO term analysis

# log10(2) = 0.3
GO_analysis <- function(DE_results_Seurat_list , p_adj_cutoff = 0.05 , avg_logFC_cutoff = 0.3 ){ 
  require("clusterProfiler")
  
  if( length(DE_results_Seurat_list) < 1){
    warning("No entries in DE_results_Seurat_list")
    invisible(DE_results_Seurat_list)
  }
  
  de_genes_list <-list(NULL)
  for(i in seq_len(length(DE_results_Seurat_list)) ){
    de_genes <- DE_results_Seurat_list[[i]] %>% tibble::rownames_to_column("gene") %>% dplyr::filter( p_val_adj < p_adj_cutoff & abs(avg_logFC) > avg_logFC_cutoff ) %>% dplyr::pull(gene)
    
    de_genes_list[[ names(DE_results_Seurat_list[i]) ]] <- bitr(geneID = de_genes , fromType = "SYMBOL" , toType = "ENTREZID" , OrgDb = "org.Mm.eg.db" )[,"ENTREZID"]
  }
    # available ontologies are:
    # BP - biological_process
    # CC - cellular_component
    # MF - molecular_function
    go_results <- compareCluster(geneClusters = de_genes_list , fun = "enrichGO" , OrgDb = "org.Mm.eg.db" , ont = "BP", pvalueCutoff = 0.05  )
  
    clusterProfiler::plot(go_results )
      
  return(go_results)
}

Load the results of the differential expression tests and check the associated GO terms by enrichment analysis

Go Term analysis for Differentially expressed genes between the individual celltypes (finding marker genes for the celltypes)

DE_celltype_genes_qNSC1 <- read.csv(file = "celltype_markers/celltype_qNSC1_upregulated_markers_10X2_young_old.csv"  , header = TRUE , row.names = 1 )
DE_celltype_genes_qNSC2 <- read.csv(file = "celltype_markers/celltype_qNSC2_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
DE_celltype_genes_aNSC0 <- read.csv(file = "celltype_markers/celltype_aNSC0_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
DE_celltype_genes_aNSC1 <- read.csv(file = "celltype_markers/celltype_aNSC1_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
DE_celltype_genes_aNSC2 <- read.csv(file = "celltype_markers/celltype_aNSC2_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
DE_celltype_genes_TAP <- read.csv(file = "celltype_markers/celltype_TAP_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
DE_celltype_genes_NB <- read.csv(file = "celltype_markers/celltype_NB_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
DE_celltype_genes_OPC <- read.csv(file = "celltype_markers/celltype_OPC_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
DE_celltype_genes_OD <- read.csv(file = "celltype_markers/celltype_OD_upregulated_markers_10X2_young_old.csv" , header = TRUE , row.names = 1 )
celltype_order <- c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB","OPC","OD")

First we load all objects starting with DE_genes into one list with the object names becoming the list name identifiers and we filter the contained data frames for a value 0 or bigger in the avg_logFC column

df_list_DE_genes_celltypes <- mget( paste( "DE_celltype_genes" , celltype_order , sep = "_") )
df_list_DE_genes_celltypes <- lapply(df_list_DE_genes_celltypes, FUN = function(x) filter(x,avg_logFC>=0) ) 

Now we can change the name of the list fields to just the celltypes ...

names(df_list_DE_genes_celltypes) <- str_replace(string = names(df_list_DE_genes_celltypes) , pattern = "^DE_genes_" ,replacement = "" )
df_list_DE_genes_celltypes <- lapply(df_list_DE_genes_celltypes , FUN = function(x){
                                                                          rownames(x) <- NULL
                                                                          column_to_rownames(df = x, var = "gene")
                                                                        })

and run GO_analysis function on it

go_celltypes <- GO_analysis(DE_results_Seurat_list = df_list_DE_genes_celltypes )
Loading required package: clusterProfiler
Loading required package: DOSE

DOSE v3.4.0  For help: https://guangchuangyu.github.io/DOSE

If you use DOSE in published research, please cite:
Guangchuang Yu, Li-Gen Wang, Guang-Rong Yan, Qing-Yu He. DOSE: an R/Bioconductor package for Disease Ontology Semantic and Enrichment analysis. Bioinformatics 2015, 31(4):608-609

clusterProfiler v3.4.4  For help: https://guangchuangyu.github.io/clusterProfiler

If you use clusterProfiler in published research, please cite:
Guangchuang Yu., Li-Gen Wang, Yanyan Han, Qing-Yu He. clusterProfiler: an R package for comparing biological themes among gene clusters. OMICS: A Journal of Integrative Biology. 2012, 16(5):284-287.

Attaching package: ‘clusterProfiler’

The following object is masked from ‘package:purrr’:

    simplify

Loading required package: org.Mm.eg.db
Loading required package: AnnotationDbi

Attaching package: ‘AnnotationDbi’

The following object is masked from ‘package:dplyr’:

    select


'select()' returned 1:1 mapping between keys and columns
1.56% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
1.36% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
2.42% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
0.97% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
3.42% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
5.19% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
3.31% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
1.23% of input gene IDs are fail to map...'select()' returned 1:1 mapping between keys and columns
2.11% of input gene IDs are fail to map...
go_res <- go_celltypes@compareClusterResult
# Cut names in column Cluster to just the activation state: e.g. "DE_celltype_genes_qNSC1" to "qNSC1"
go_res$Cluster <- sub( go_res$Cluster , pattern = ".+_" , replacement = "" )
# write.csv(x = go_res , file = "GO_results_compare_celltypes_10X.csv")

and after simplifying the GO terms enriched in the genes

go_celltypes.sim <- simplify(go_celltypes)

Custom plot of GO categories for activation states along lineage

gg2 <- clusterProfiler::plot(go_celltypes.sim)
# gg2$data$Description <- fct_relabel( .f = gg2$data$Description , .fun = function(x){str_wrap(string = x , width = 40)} )
plotdata <- gg2$data %>% filter( p.adjust < 0.05)
levels(plotdata$Cluster) <- sub( x = levels(plotdata$Cluster) , pattern = "DE_celltype_genes_", replacement = "")
gg_clusterCompare <- ggplot(
                      data = plotdata , 
                      mapping = aes( 
                        x = Cluster , 
                        y = Description , 
                        size = GeneRatio , 
                        color = -log10(p.adjust) 
                        ) 
                      ) + 
                     geom_point() + 
                     scale_color_gradient(low = "blue" , high = "red"  , breaks = c(1,10,20,30,40) , limits = c(1,50) ) + 
                     theme_bw() + 
                     theme( 
                       axis.text.x = element_text(colour="black",size=11), 
                       axis.text.y = element_text(colour="black",size=11) 
                     )
gg_clusterCompare

Heatmaps

neuronal_lineage <- WhichCells(object = seurat_10X2 , ident.remove = c("OD","OPC"))

Heatmap with genes from Basak et al. , but ordered according to center of mass along pseudotime

markers <- c("Agt", "Slc6a9", "Etnppl", "Slc6a1", "Sparc", "Slc1a3", "Bcan", 
"Tspan7", "Htra1", "Cldn10", "Ptn", "Acsl6", "Fgfr3", "Sparcl1", 
"Atp1a2", "Gpr37l1", "Gja1", "Prnp", "Acsl3", "Aqp4", "Apoe", 
"Gm26917", "Cst3", "Clu", "Slc1a2", "Prdx6", "Mt1", "Aldoc", 
"Thbs4", "Ntrk2", "Fxyd1", "Gstm1", "Igfbp5", "S100a6", "Itm2b", 
"Sfrp1", "Dkk3", "C4b", "Acot1", "Luc7l3", "Ckb", "Cpe", "Dbi", 
"Miat", "Lima1", "Pabpc1", "Ascl1", "Rpl12", "Mycn", "Olig2", 
"Pcna", "Hsp90aa1", "Hnrnpab", "Ran", "Ppia", "Eef1a1", "Ptma", 
"Rpl41", "Npm1", "Rpsa", "Fabp7", "Egfr", "Mki67", "Dlx2", "Dlx1", 
"Cdca3", "Dlx1as", "Nrep", "Tubb2b", "Dcx", "Btg1", "Nfib", "Gad1", 
"Ndrg4", "Snap25", "Syt1", "Rbfox3", "Tmsb10", "Stmn2", "Cd24a", 
"Dlx6os1", "Tubb5", "Tubb3", "Ccnd2", "Hmgn2", "H2afz", "Sox11", 
"Tuba1b", "Tmsb4x", "Stmn1", "Tpt1", "Rpl18a")

Fetch the data

expr <- FetchData(object = seurat_10X2 , vars.all = markers , cells.use = neuronal_lineage )
plotDf_expr <-  FetchData(object = seurat_10X2 , vars.all = c("Pseudotime") , cells.use = neuronal_lineage ) %>%
                rownames_to_column(var = "cellbarcode") %>% 
                full_join( as.data.frame(expr) %>% 
                  rownames_to_column(var = "cellbarcode") 
                , by = "cellbarcode" ) %>% 
                filter(! is.na(Pseudotime)) %>%
                arrange(Pseudotime)
plotDf_expr 
expr_mat <- plotDf_expr %>% dplyr::select(-cellbarcode,-Pseudotime) %>% as.matrix()
center_of_mass <- apply(X = expr_mat ,MARGIN = 2 , FUN = function(x){ min(which(sum(x)/2 <= cumsum(x))) } )
markers_center_of_mass <- names(sort(center_of_mass))
DoHeatmap(object = seurat_10X2 , genes.use = markers_center_of_mass , slim.col.label = TRUE , col.low = "blue" , col.mid = "white" , col.high = "red" , group.label.rot = TRUE, group.order = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB","OPC","OD") , use.scaled = TRUE , cex.row = 3 , group.cex = 10 ) 

Heatmap with marker genes differentially expressed in the celltypes

Rename the objects and convert column gene to rownames in every data.frame in the list

df_list_DE_genes_celltypes <- mget( paste( "DE_celltype_genes" , c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB") , sep = "_") )
df_list_DE_genes_celltypes <- lapply(df_list_DE_genes_celltypes, FUN = function(x) filter(x,avg_logFC>=0) ) 
names(df_list_DE_genes_celltypes) <- str_replace(string = names(df_list_DE_genes_celltypes) , pattern = "^DE_genes_" ,replacement = "" )
# Get all names from the sig. differentially expressed celltype markers genes
markers_center_of_mass_de <- lapply(df_list_DE_genes_celltypes , FUN = function(x){ 
                                                                          dplyr::filter(x,  p_val_adj < 0.05 ) %>% 
                                                                          pull(gene) } )
markers_center_of_mass_de <- unique(unlist(markers_center_of_mass_de))
expr_de <- FetchData(object = seurat_10X2 , vars.all = markers_center_of_mass_de , cells.use = neuronal_lineage )
plotDf_expr_de <-  FetchData(object = seurat_10X2 , vars.all = c("Pseudotime") , cells.use = neuronal_lineage ) %>%
                rownames_to_column(var = "cellbarcode") %>% 
                full_join( as.data.frame(expr_de) %>% 
                  rownames_to_column(var = "cellbarcode") 
                , by = "cellbarcode" ) %>% 
                filter(! is.na(Pseudotime)) %>%
                arrange(Pseudotime)

Convert to matrix

expr_mat_de <- plotDf_expr_de %>% dplyr::select(-cellbarcode,-Pseudotime) %>% as.matrix()

Calculate the center of mass for every gene

center_of_mass_de <- apply(X = expr_mat_de ,MARGIN = 2 , FUN = function(x){ min(which(sum(x)/2 <= cumsum(x))) } )
length(center_of_mass_de)
[1] 2527
markers_center_of_mass_de <- names(sort(center_of_mass_de))
markers_center_of_mass_de_wts <- sort(center_of_mass_de)

Now we also take the markers for the Oligodendrocytes and their Progenitors and append them

genes_OD <- as.character( DE_celltype_genes_OD %>% dplyr::filter( p_val_adj < 0.05 ) %>% pull(gene) )
genes_OD_wts <- rep(1, length(genes_OD))
names(genes_OD_wts) <-  genes_OD
genes_OPC <- as.character( DE_celltype_genes_OPC %>% dplyr::filter( p_val_adj < 0.05 ) %>% pull(gene) )
genes_OPC_wts <- rep(0, length(genes_OPC))
names(genes_OPC_wts) <-  genes_OPC

Append the genes to the list of markers

markers_center_of_mass_de <- c(markers_center_of_mass_de, genes_OPC , genes_OD )
markers_center_of_mass_de <- unique(markers_center_of_mass_de)
markers_center_of_mass_de_wts <- c(markers_center_of_mass_de_wts , genes_OPC_wts , genes_OD_wts)
markers_center_of_mass_de_wts <- markers_center_of_mass_de_wts[ unique(names(markers_center_of_mass_de_wts)) ]

Plot Heatmap

DoHeatmap(object = seurat_10X2 , genes.use = markers_center_of_mass_de , slim.col.label = TRUE , group.cex = 10 ,  col.low = "blue" , col.mid = "white" , col.high = "red" , group.label.rot = TRUE, group.order = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB","OPC","OD") , use.scaled = TRUE , cex.row = 0 ) 

Plot average expression per gene young vs old

Split the 10X data by age

seurat_10X2_young <- SubsetData( object = seurat_10X2 , subset.name = "age_num" ,  accept.low = 1.5 )
seurat_10X2_old <- SubsetData( object = seurat_10X2 , subset.name = "age_num" ,  accept.high = 1.5 )

All cells

library(ggrepel)
old_avg <- AverageExpression(object = SetIdent( object = seurat_10X2_old ,ident.use = "old" ) ) %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster old"
young_avg <- AverageExpression(object = SetIdent( object = seurat_10X2_young ,ident.use = "young") )  %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster young"
avg <- full_join(x = old_avg  , y = young_avg  , by = "gene")
avg$old <- avg$old + 1
avg$young <- avg$young + 1
avg_mut <- mutate(avg , rt=old/young ) %>% filter(rt > 2  | rt < 0.5)
mx <- max(c(avg$old,avg$young))+5
ggplot(data = avg , mapping = aes(x = young , y = old )) + geom_point(color = "grey") + coord_equal() + geom_line(data = data.frame(x = c(1,1000) , y = c(1,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , size= 0.3 ) + geom_line(data = data.frame(x = c(2,1000) , y = c(1,500) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + geom_line(data = data.frame(x = c(1,500) , y = c(2,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + scale_x_log10() + scale_y_log10() + annotation_logticks() + geom_point(data = avg_mut , size = 2  , fill = "black" , pch = 21  ) + ggtitle("Comparison of average gene expression\nbetween young and old")

Only lineage

library(ggrepel)
old_avg <- AverageExpression(object = SetIdent( object = SubsetData( seurat_10X2_old , ident.use = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB") )  ,ident.use = "old" ) ) %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster old"
young_avg <- AverageExpression(object = SetIdent( object = SubsetData(object = seurat_10X2_young , ident.use = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB") ) ,ident.use = "young") )  %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster young"
avg <- full_join(x = old_avg  , y = young_avg  , by = "gene")
avg$old <- avg$old + 1
avg$young <- avg$young + 1
avg_mut <- mutate(avg , rt=old/young ) %>% filter(rt > 2  | rt < 0.5)
mx <- max(c(avg$old,avg$young))+5
ggplot(data = avg , mapping = aes(x = young , y = old )) + geom_point( color = "grey" ) + coord_equal() + geom_line(data = data.frame(x = c(1,1000) , y = c(1,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , size= 0.3 ) + geom_line(data = data.frame(x = c(2,1000) , y = c(1,500) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + geom_line(data = data.frame(x = c(1,500) , y = c(2,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + scale_x_log10() + scale_y_log10() + annotation_logticks() + geom_point(data = avg_mut , size = 2  , fill = "black" , pch = 21   ) + ggtitle("Comparison of average gene expression\nbetween young and old - qNSC1 to NB")

Only NSCs

library(ggrepel)
old_avg <- AverageExpression(object = SetIdent( object = SubsetData( seurat_10X2_old , ident.use = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2") )  ,ident.use = "old" ) ) %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster old"
young_avg <- AverageExpression(object = SetIdent( object = SubsetData(object = seurat_10X2_young , ident.use = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2") ) ,ident.use = "young") )  %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster young"
avg <- full_join(x = old_avg  , y = young_avg  , by = "gene")
avg$old <- avg$old + 1
avg$young <- avg$young + 1
avg_mut <- mutate(avg , rt=old/young ) %>% filter(rt > 2  | rt < 0.5)
mx <- max(c(avg$old,avg$young))+5
ggplot(data = avg , mapping = aes(x = young , y = old )) + geom_point( color = "grey") + coord_equal() + geom_line(data = data.frame(x = c(1,1000) , y = c(1,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , size= 0.3 ) + geom_line(data = data.frame(x = c(2,1000) , y = c(1,500) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + geom_line(data = data.frame(x = c(1,500) , y = c(2,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + scale_x_log10() + scale_y_log10() + annotation_logticks() + geom_point(data = avg_mut , size = 2  , fill = "black" , pch = 21   ) + ggtitle("Comparison of average gene expression\nbetween young and old - only neural stem cells")

Only Oligodendrocytes and Oligodendrocyte progenitors

library(ggrepel)
old_avg <- AverageExpression(object = SetIdent( object = SubsetData( seurat_10X2_old , ident.use = c("OD","OPC") )  ,ident.use = "old" ) ) %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster old"
young_avg <- AverageExpression(object = SetIdent( object = SubsetData(object = seurat_10X2_young , ident.use = c("OD","OPC") ) ,ident.use = "young") )  %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster young"
avg <- full_join(x = old_avg  , y = young_avg  , by = "gene")
avg$old <- avg$old + 1
avg$young <- avg$young + 1
avg_mut <- mutate(avg , rt=old/young ) %>% filter(rt > 2  | rt < 0.5)
mx <- max(c(avg$old,avg$young))+5
ggplot(data = avg , mapping = aes(x = young , y = old )) + geom_point(color = "grey") + coord_equal() + geom_line(data = data.frame(x = c(1,1000) , y = c(1,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , size= 0.3 ) + geom_line(data = data.frame(x = c(2,1000) , y = c(1,500) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + geom_line(data = data.frame(x = c(1,500) , y = c(2,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + scale_x_log10() + scale_y_log10() + annotation_logticks() + geom_point(data = avg_mut , size = 1.5  , fill = "black" , pch = 21   ) + ggtitle("Comparison of average gene expression\nbetween young and old - OPC and OD")

Only Oligodendrocyte progenitors

library(ggrepel)
old_avg <- AverageExpression(object = SetIdent( object = SubsetData( seurat_10X2_old , ident.use = c("OPC") )  ,ident.use = "old" ) ) %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster old"
young_avg <- AverageExpression(object = SetIdent( object = SubsetData(object = seurat_10X2_young , ident.use = c("OPC") ) ,ident.use = "young") )  %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster young"
avg <- full_join(x = old_avg  , y = young_avg  , by = "gene")
avg$old <- avg$old + 1
avg$young <- avg$young + 1
avg_mut <- mutate(avg , rt=old/young ) %>% filter(rt > 2  | rt < 0.5)
mx <- max(c(avg$old,avg$young))+5
ggplot(data = avg , mapping = aes(x = young , y = old )) + geom_point(color = "grey") + coord_equal() + geom_line(data = data.frame(x = c(1,1000) , y = c(1,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , size= 0.3 ) + geom_line(data = data.frame(x = c(2,1000) , y = c(1,500) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + geom_line(data = data.frame(x = c(1,500) , y = c(2,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + scale_x_log10() + scale_y_log10() + annotation_logticks() + geom_point(data = avg_mut , size = 2 , fill = "black" , pch = 21   ) + ggtitle("Comparison of average gene expression\nbetween young and old - OPC")

Only Oligodendrocytes

library(ggrepel)
old_avg <- AverageExpression(object = SetIdent( object = SubsetData( seurat_10X2_old , ident.use = c("OD") )  ,ident.use = "old" ) ) %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster old"
young_avg <- AverageExpression(object = SetIdent( object = SubsetData(object = seurat_10X2_young , ident.use = c("OD") ) ,ident.use = "young") )  %>% rownames_to_column("gene")
[1] "Finished averaging RNA for cluster young"
avg <- full_join(x = old_avg  , y = young_avg  , by = "gene")
avg$old <- avg$old + 1
avg$young <- avg$young + 1
avg_mut <- mutate(avg , rt=old/young ) %>% filter(rt > 2  | rt < 0.5)
mx <- max(c(avg$old,avg$young))+5
ggplot(data = avg , mapping = aes(x = young , y = old )) + geom_point(color = "grey") + coord_equal() + geom_line(data = data.frame(x = c(1,1000) , y = c(1,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , size= 0.3 ) + geom_line(data = data.frame(x = c(2,1000) , y = c(1,500) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + geom_line(data = data.frame(x = c(1,500) , y = c(2,1000) ) , mapping = aes(x=x,y=y) , alpha = 1 , color = "grey40" , linetype = 2 ) + scale_x_log10() + scale_y_log10() + annotation_logticks() + geom_point(data = avg_mut , size = 2 , fill = "black" , pch = 21   ) + ggtitle("Comparison of average gene expression\nbetween young and old - OD")

Euclidean distances between clusters q1 and q2, q2 and a0 , a0 and a1 , opc and od

data_selected_celltypes <- FetchData(object = seurat_10X2 , vars.all = c("PC1","PC2","PC3","PC4","PC5","PC6","PC7","PC8") , cells.use = WhichCells(object = seurat_10X2 , ident = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB","OPC","OD")))

Calculate euclidean distance

dstmat_selected_celltypes <- as.matrix( dist(data_selected_celltypes) )

View into the distance matrix

head(dstmat_selected_celltypes[1:5,1:5])
                   AAAGTAGAGTAATCCC-1 AAAGTAGCAGCGAACA-1 AAAGTAGTCGCAAGCC-1 AACCGCGAGCACCGTC-1 AACCGCGCACCAGCAC-1
AAAGTAGAGTAATCCC-1           0.000000           3.242801           6.257834           6.649707           4.740841
AAAGTAGCAGCGAACA-1           3.242801           0.000000           4.419544           5.633196           3.580018
AAAGTAGTCGCAAGCC-1           6.257834           4.419544           0.000000           4.488831           3.203712
AACCGCGAGCACCGTC-1           6.649707           5.633196           4.488831           0.000000           6.068005
AACCGCGCACCAGCAC-1           4.740841           3.580018           3.203712           6.068005           0.000000

Get the names of the cells from the different celltypes

q1 <- WhichCells(object = seurat_10X2 , ident = c("qNSC1"))
q2 <- WhichCells(object = seurat_10X2 , ident = c("qNSC2"))
a0 <- WhichCells(object = seurat_10X2 , ident = c("aNSC0"))
a1 <- WhichCells(object = seurat_10X2 , ident = c("aNSC1"))
a2 <- WhichCells(object = seurat_10X2 , ident = c("aNSC2"))
tap <- WhichCells(object = seurat_10X2 , ident = c("TAP"))
nb  <- WhichCells(object = seurat_10X2 , ident = c("NB"))
opc <- WhichCells(object = seurat_10X2 , ident = c("OPC"))
od <- WhichCells(object = seurat_10X2 , ident = c("OD"))

Select the submatrix which contains the distances between celltypes

q1 and q2

dstmat_q1q2 <- dstmat_selected_celltypes[q1,q2]
hist(dstmat_q1q2 , xlab = "Euclidean Distance" , main = "Histogram: Euclidean Distances from qNSC1 to qNSC2" )
abline(v = mean(dstmat_q1q2) , col = "red")

q2 and a0

dstmat_q2a0 <- dstmat_selected_celltypes[q2,a0]
hist(dstmat_q2a0, xlab = "Euclidean Distance" , main = "Histogram: Euclidean Distances from qNSC2 to aNSC0" )
abline(v = mean(dstmat_q2a0) , col = "red")

a0 and a1

dstmat_a0a1 <- dstmat_selected_celltypes[a0,a1]
hist(dstmat_a0a1, xlab = "Euclidean Distance" , main = "Histogram: Euclidean Distances from aNSC0 to aNSC1" )
abline(v = mean(dstmat_a0a1) , col = "red")

opc and od

dstmat_opc_od <- dstmat_selected_celltypes[opc,od]
hist(dstmat_opc_od , xlab = "Euclidean Distance" , main = "Histogram: Euclidean Distances from OPC to OD" )
abline(v = mean(dstmat_opc_od) , col = "red")

q1 and od

dstmat_q1_od <- dstmat_selected_celltypes[q1,od]
hist(dstmat_q1_od , xlab = "Euclidean Distance" , main = "Histogram: Euclidean Distances from qNSC1 to OD" )
abline(v = mean(dstmat_q1_od) , col = "red")

q2 and od

dstmat_q2_od <- dstmat_selected_celltypes[q2,od]
hist(dstmat_q2_od , xlab = "Euclidean Distance" , main = "Histogram: Euclidean Distances from qNSC2 to OD" )
abline(v = mean(dstmat_q2_od) , col = "red")

Density plot of euclidean distances

df_hist<- bind_rows(
  data.frame( comparison = "qNSC1 vs qNSC2" , count = as.vector(dstmat_q1q2) ) ,
  data.frame( comparison = "qNSC2 vs aNSC0" , count = as.vector(dstmat_q2a0) ) ,
  data.frame( comparison = "aNSC0 vs aNSC1" , count = as.vector(dstmat_a0a1) ) ,
  data.frame( comparison = "OPC vs OD" , count = as.vector(dstmat_opc_od) ) ,
  data.frame( comparison = "qNSC1 vs OD" , count = as.vector(dstmat_q1_od) ) ,
  data.frame( comparison = "aNSC1 vs aNSC2" , count = as.vector(dstmat_selected_celltypes[a1,a2]) ),
  data.frame( comparison = "aNSC2 vs TAP" , count =as.vector(dstmat_selected_celltypes[a2,tap]) ),
  data.frame( comparison = "TAP vs NB" , count =as.vector(dstmat_selected_celltypes[tap,nb]) )
) 
Unequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector
df_hist$comparison <- factor(df_hist$comparison , levels = c("OPC vs OD","qNSC1 vs OD","qNSC1 vs qNSC2","qNSC2 vs aNSC0","aNSC0 vs aNSC1","aNSC1 vs aNSC2","aNSC2 vs TAP","TAP vs NB"))
ggplot(data = df_hist ) + geom_density(aes(x=count,color=comparison, fill = comparison) , alpha = 0.25 ) + xlab("euclidean distance")  + facet_wrap(facets = "comparison" , ncol = 1) + geom_vline(xintercept=19.7)

PCA plot of 10X data

celltype_colors <- c( qNSC1 = "steelblue" , qNSC2 = "steelblue1" , aNSC0 = "tomato" , aNSC1 = "sienna1", aNSC2 = "sienna3" , TAP = "green" , NB = "yellow" , OPC = "pink" , OD = "violet")
age_colors <- c(young = "yellowgreen" , old = "slateblue")
seurat_10X2_age_ident <- SetAllIdent(object = seurat_10X2 , id = "age")

Colored by celltype

Calculate percentage of variance for all PCs

sd <- GetDimReduction(object = seurat_10X2 , reduction.type = "pca" , slot = "sdev")
pov <- round( (sd^2/sum(sd^2))*100 , digits = 1 )
pc_1_2 <- PCAPlot(object = seurat_10X2, 1, 2 , pt.size = 0.5 , do.return = TRUE) + scale_color_manual(values = celltype_colors , breaks = names(celltype_colors) )   + xlab(paste("PC1 (",pov[1],"% )")) + ylab(paste("PC2 (",pov[2], "% )")) + coord_equal()
pc_1_2

pc_1_3 <- PCAPlot(object = seurat_10X2, 1, 3 , pt.size = 0.5 , do.return = TRUE) + scale_color_manual(values = celltype_colors , breaks = names(celltype_colors) )  + xlab(paste("PC1 (",pov[1],"% )")) + ylab(paste("PC3 (",pov[3], "% )"))  + coord_equal()
pc_1_3

pc_2_3 <- PCAPlot(object = seurat_10X2, 2,3 , pt.size = 0.5 , do.return = TRUE) + scale_color_manual(values = celltype_colors , breaks = names(celltype_colors) )  + xlab(paste("PC2 (",pov[2],"% )")) + ylab(paste("PC3 (",pov[3], "% )"))  + coord_equal()
pc_2_3

pc_1_4 <- PCAPlot(object = seurat_10X2, 1,4 , pt.size = 0.5, do.return = TRUE) + scale_color_manual(values = celltype_colors , breaks = names(celltype_colors) )  + xlab(paste("PC1 (",pov[1],"% )")) + ylab(paste("PC4 (",pov[4], "% )"))  + coord_equal()
pc_1_4

Colored by age

PCAPlot(object = seurat_10X2_age_ident, 1, 2 , pt.size = 0.5, do.return = TRUE) + scale_color_manual(values = age_colors , breaks = names(age_colors) )   + xlab(paste("PC1 (",pov[1],"% )")) + ylab(paste("PC2 (",pov[2], "% )"))  + coord_equal()

pc_1_3_age <- PCAPlot(object = seurat_10X2_age_ident, 1, 3 , pt.size = 0.5 , do.return = TRUE) + scale_color_manual(values = age_colors , breaks = names(age_colors) )  + xlab(paste("PC1 (",pov[1],"% )")) + ylab(paste("PC3 (",pov[3], "% )"))  + coord_equal()
pc_1_3_age

PCAPlot(object = seurat_10X2_age_ident, 2, 3 , pt.size = 0.5 , do.return = TRUE) + scale_color_manual(values = age_colors , breaks = names(age_colors) ) + xlab(paste("PC2 (",pov[2],"% )")) + ylab(paste("PC3 (",pov[3], "% )"))  + coord_equal()

PCAPlot(object = seurat_10X2_age_ident, 1, 4 , pt.size = 0.5 , do.return = TRUE) + scale_color_manual(values = age_colors , breaks = names(age_colors) )  + xlab(paste("PC1 (",pov[1],"% )")) + ylab(paste("PC4 (",pov[4], "% )"))  + coord_equal()

PCA plots grid

grid.arrange(pc_1_3,pc_1_3_age,pc_1_2,pc_2_3)

t-SNE plot of all cells

df_plot_tSNE <- FetchData(object = seurat_10X2, vars.all = c("tSNE_1","tSNE_2","age","celltype") ) %>% 
            rownames_to_column("cellbarcode")
gg_tsne <- ggplot(
      data = df_plot_tSNE,
      mapping = aes(
        x = tSNE_1,
        y = tSNE_2,
        fill = factor( celltype , levels = names(celltype_colors) )
        )
     ) +
  geom_point( pch = 21 , color = "black" , size = 2 ) +
  scale_fill_manual(values = celltype_colors , name = "Activation state" ) +  
  guides(color = "none" ) +
  theme(legend.position="none") + 
  coord_equal()
gg_tsne

gg_tsne_age <- ggplot(
      data = df_plot_tSNE,
      mapping = aes(
        x = tSNE_1,
        y = tSNE_2,
        fill = factor( age , levels = names(age_colors) )
        )
     ) +
  geom_point( pch = 21 , color = "black" , size = 2 ) +
  scale_fill_manual(values = age_colors , name = "Age" ) +  
  guides(color = "none") + 
  theme(legend.position="none") + 
  coord_equal()
gg_tsne_age

Make PCA and tSNE plots for each subpopulation

PCAPlot_seurat <- function(object , dim1 = 1, dim2 = 2 , scale_pcs_by_sd = TRUE ){
  gg_pca <- PCAPlot(object = object , dim.1 = dim1, dim.2 = dim2 , cols.use = celltype_colors , pt.shape = "age", do.return = TRUE )
  
  sd <- GetDimReduction(object = object , reduction.type = "pca" , slot = "sdev")
  pov <- round( (sd^2/sum(sd^2))*100 , digits = 1 )
  
  if(scale_pcs_by_sd){
    gg_pca$data$x <- gg_pca$data$x * sd[[dim1]]
    gg_pca$data$y <- gg_pca$data$y * sd[[dim2]]
  }
  
  xlimits <- NULL
  ylimits <- NULL
    
  x_range <- range(gg_pca$data$x)
  x_diff <- x_range[2]-x_range[1] 
  
  y_range  <- range(gg_pca$data$y)
  y_diff <- y_range[2]-y_range[1] 
  if(x_diff > y_diff){
    
    offset <- (( x_diff - y_diff ) / 2 )
    
    y_range[1] <- y_range[1] - offset
    y_range[2] <- y_range[2] + offset
    ylimits <- y_range
        
  }else if( y_diff > x_diff ){
    
    offset <- (( y_diff - x_diff ) / 2 )
    
    x_range[1] <- x_range[1] - offset
    x_range[2] <- x_range[2] + offset
  
    xlimits <- x_range
    
  }
    
    
  gg <- ggplot(data = gg_pca$data , aes(x = x , y = y , fill = pt.shape  , shape = pt.shape) ) + geom_point(size = 2, colour = "black") + theme_bw() + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank() ) + scale_fill_manual(values = age_colors , limits = names(age_colors) ,  name = "Age") + scale_shape_manual(values = c(old = 24 , young = 21) , limits = names(age_colors) , name = "Age") + coord_equal()  + xlab(paste("PC",dim1," (",pov[dim1],"%)")) + ylab(paste("PC",dim2," (",pov[dim2], "%)")) + ggtitle(paste0(unique(gg_pca$data$ident)) ) + guides(color = "none")
  
  if(!is.null(xlimits)){gg <- gg + xlim( xlimits )}
  if(!is.null(ylimits)){gg <- gg + ylim( ylimits )}
  
  gg
}
TSNEPlot_Seurat <- function(x , title = ""){
  xlimits <- NULL
  ylimits <- NULL
    
  x_range <- range(x$tSNE_1)
  x_diff <- x_range[2]-x_range[1] 
  
  y_range  <- range(x$tSNE_2)
  y_diff <- y_range[2]-y_range[1] 
  if(x_diff > y_diff){
    
    offset <- (( x_diff - y_diff ) / 2 )
    
    y_range[1] <- y_range[1] - offset
    y_range[2] <- y_range[2] + offset
    ylimits <- y_range
        
  }else if( y_diff > x_diff ){
    
    offset <- (( y_diff - x_diff ) / 2 )
    
    x_range[1] <- x_range[1] - offset
    x_range[2] <- x_range[2] + offset
  
    xlimits <- x_range
  }
    
  gg <- ggplot(data = x , mapping = aes(x = tSNE_1 , y = tSNE_2 , fill = age  , shape = age)) + geom_point(size = 2, colour = "black") + scale_fill_manual(values = c( "old" =  "slateblue", "young" = "yellowgreen") , labels = c( "old" , "young" ) , name = "Age" ) + labs( x = "tSNE 1" , y = "tSNE 2" ) + coord_equal() + guides(color = "none") + scale_shape_manual(values = c( "old" = 24 , "young" = 21) , labels = c( "old" , "young" ) , name = "Age") + ggtitle( title )
  
  if(!is.null(xlimits)){gg <- gg + xlim( xlimits )}
  if(!is.null(ylimits)){gg <- gg + ylim( ylimits )}
  
  gg
}
seurat_10X2_qNSC1 <- SubsetData(object = seurat_10X2 , ident.use = "qNSC1")
seurat_10X2_qNSC1 <- FindVariableGenes(object = seurat_10X2_qNSC1 , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_qNSC1 <- RunPCA(object = seurat_10X2_qNSC1 , do.print = FALSE )
pca_q1 <- PCAPlot_seurat( object = seurat_10X2_qNSC1 , dim1 = 1, dim2 = 2 )
pca_q1

PCAPlot_seurat( object = seurat_10X2_qNSC1 , dim1 = 2, dim2 = 3 )

PC_top50genes_qNSC1<- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_qNSC1 , pc.use = x , num.genes = 50 )} ) )
seurat_10X2_qNSC1 <- RunTSNE(object = seurat_10X2_qNSC1 , dims.use = 1:3 , seed.use = 1 )
TSNEPlot(object = seurat_10X2_qNSC1 ) 

x <- FetchData( object =  seurat_10X2_qNSC1 , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_qNSC1 <- TSNEPlot_Seurat(x = x , title = "qNSC1") 
gg_tsne_qNSC1

seurat_10X2_qNSC2 <- SubsetData(object = seurat_10X2 , ident.use = "qNSC2")
seurat_10X2_qNSC2 <- FindVariableGenes(object = seurat_10X2_qNSC2 , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_qNSC2 <- RunPCA(object = seurat_10X2_qNSC2 , do.print = FALSE )
pca_q2 <- PCAPlot_seurat( object = seurat_10X2_qNSC2 , dim1 = 1, dim2 = 2 )
pca_q2

PCAPlot_seurat( object = seurat_10X2_qNSC2 , dim1 = 2, dim2 = 3 )

PC_top50genes_qNSC2<- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_qNSC2 , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_qNSC2 , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_qNSC2 <- TSNEPlot_Seurat(x = x , title = "qNSC2") 
gg_tsne_qNSC2

seurat_10X2_aNSC0 <- SubsetData(object = seurat_10X2 , ident.use = "aNSC0")
seurat_10X2_aNSC0 <- FindVariableGenes(object = seurat_10X2_aNSC0 , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_aNSC0 <- RunPCA(object = seurat_10X2_aNSC0 , do.print = FALSE )
pca_a0 <- PCAPlot_seurat( object = seurat_10X2_aNSC0 , dim1 = 1, dim2 = 2 )
pca_a0

PCAPlot_seurat( object = seurat_10X2_aNSC0 , dim1 = 2, dim2 = 3 )

PC_top50genes_aNSC0<- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_aNSC0 , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_aNSC0 , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_aNSC0 <- TSNEPlot_Seurat(x = x , title = "aNSC0") 
gg_tsne_aNSC0

seurat_10X2_aNSC1 <- SubsetData(object = seurat_10X2 , ident.use = "aNSC1")
seurat_10X2_aNSC1 <- FindVariableGenes(object = seurat_10X2_aNSC1 , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_aNSC1 <- RunPCA(object = seurat_10X2_aNSC1 , do.print = FALSE )
pca_a1 <- PCAPlot_seurat( object = seurat_10X2_aNSC1 , dim1 = 1, dim2 = 2 )
pca_a1

PCAPlot_seurat( object = seurat_10X2_aNSC1 , dim1 = 2, dim2 = 3 )

PC_top50genes_aNSC1<- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_aNSC1 , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_aNSC1 , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_aNSC1 <- TSNEPlot_Seurat(x = x , title = "aNSC1") 
gg_tsne_aNSC1

seurat_10X2_aNSC2 <- SubsetData(object = seurat_10X2 , ident.use = "aNSC2")
seurat_10X2_aNSC2 <- FindVariableGenes(object = seurat_10X2_aNSC2 , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_aNSC2 <- RunPCA(object = seurat_10X2_aNSC2 , do.print = FALSE )
pca_a2 <- PCAPlot_seurat( object = seurat_10X2_aNSC2 , dim1 = 1, dim2 = 2 )
pca_a2

PCAPlot_seurat( object = seurat_10X2_aNSC2 , dim1 = 2, dim2 = 3 )

PC_top50genes_aNSC2 <- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_aNSC2 , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_aNSC2 , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_aNSC2 <- TSNEPlot_Seurat(x = x , title = "aNSC2") 
gg_tsne_aNSC2

seurat_10X2_TAP <- SubsetData(object = seurat_10X2 , ident.use = "TAP")
seurat_10X2_TAP <- FindVariableGenes(object = seurat_10X2_TAP , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_TAP <- RunPCA(object = seurat_10X2_TAP , do.print = FALSE )
pca_tap <- PCAPlot_seurat( object = seurat_10X2_TAP , dim1 = 1, dim2 = 2 )
pca_tap

PCAPlot_seurat( object = seurat_10X2_TAP , dim1 = 2, dim2 = 3 )

PC_top50genes_TAP <- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_TAP , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_TAP , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_TAP <- TSNEPlot_Seurat(x = x , title = "TAP") 
gg_tsne_TAP

seurat_10X2_NB <- SubsetData(object = seurat_10X2 , ident.use = "NB")
seurat_10X2_NB <- FindVariableGenes(object = seurat_10X2_NB , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_NB <- RunPCA(object = seurat_10X2_NB , do.print = FALSE )
pca_NB <- PCAPlot_seurat( object = seurat_10X2_NB , dim1 = 1, dim2 = 2 )
pca_NB

PCAPlot_seurat( object = seurat_10X2_NB , dim1 = 2, dim2 = 3 )

PC_top50genes_NB <- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_NB , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_NB , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_NB <- TSNEPlot_Seurat(x = x , title = "NB") 
gg_tsne_NB

seurat_10X2_OPC <- SubsetData(object = seurat_10X2 , ident.use = "OPC")
seurat_10X2_OPC <- FindVariableGenes(object = seurat_10X2_OPC , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_OPC <- RunPCA(object = seurat_10X2_OPC , do.print = FALSE )
pca_OPC <- PCAPlot_seurat( object = seurat_10X2_OPC , dim1 = 1, dim2 = 2 )
pca_OPC

PCAPlot_seurat( object = seurat_10X2_OPC , dim1 = 2, dim2 = 3 )

PC_top50genes_OPC <- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_OPC , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_OPC , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_OPC <- TSNEPlot_Seurat(x = x , title = "OPC") 
gg_tsne_OPC

seurat_10X2_OD <- SubsetData(object = seurat_10X2 , ident.use = "OD")
seurat_10X2_OD <- FindVariableGenes(object = seurat_10X2_OD , mean.function = ExpMean, dispersion.function = LogVMR)
Calculating gene means
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0%   10   20   30   40   50   60   70   80   90   100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|

seurat_10X2_OD <- RunPCA(object = seurat_10X2_OD , do.print = FALSE )
pca_OD <- PCAPlot_seurat( object = seurat_10X2_OD , dim1 = 1, dim2 = 2 )
pca_OD

PCAPlot_seurat( object = seurat_10X2_OD , dim1 = 2, dim2 = 3 )

PC_top50genes_OD <- bind_cols( lapply( list(PC1 = 1, PC2 = 2 , PC3 = 3) ,  FUN=function(x){PCTopGenes(object = seurat_10X2_OD , pc.use = x , num.genes = 50 )} ) )
x <- FetchData( object =  seurat_10X2_OD , vars.all = c("tSNE_1","tSNE_2","age") )
gg_tsne_OD <- TSNEPlot_Seurat(x = x , title = "OD") 
gg_tsne_OD

Gather all PCA plots

grid.arrange(pca_q1,pca_q2,pca_a0,pca_a1,pca_a2,pca_tap,pca_NB,pca_OPC,pca_OD)

Gather all t-SNE plots

grid.arrange(gg_tsne_qNSC1 , gg_tsne_qNSC2 , gg_tsne_aNSC0 , gg_tsne_aNSC1 , gg_tsne_aNSC2 , gg_tsne_TAP , gg_tsne_NB , gg_tsne_OPC , gg_tsne_OD )

Euclidean Distances between young and old cells

Which celltypes do we have?

celltypes <- names(celltype_colors)
names(celltypes) <- celltypes 

Extract the cell annotation from the seurat object

anno <- FetchData(object = seurat_10X2 , vars.all = c("age","celltype") )

Make a list of matrices with expression data per celltype

data_matrix_celltypes <- lapply( X = celltypes, FUN = function(x){
  
                        # data_10X2_celltype <- FetchData(object = seurat_10X2 , vars.all = rownames(seurat_10X2@data) , cells.use = WhichCells(object = seurat_10X2 , ident = x) )
  
                        data_10X2_celltype <- FetchData(object = seurat_10X2 , vars.all = c("PC1","PC2","PC3","PC4","PC5","PC6","PC7","PC8") , cells.use = WhichCells(object = seurat_10X2 , ident = x)  )
                        
                        data_10X2_celltype
                      })

Now we calculate the euclidean distance between all the samples from one celltype

euc_dist_celltypes <- lapply( data_matrix_celltypes , FUN = function(x){as.matrix(dist(x = x, method = "euclidean" , upper = TRUE))} )

Density of euclidean distances

df.plot.list <- lapply( X = euc_dist_celltypes, FUN = function(x){
                                            x %>% as.data.frame() %>% rownames_to_column("cell1") %>% gather(key = "cell2" , value = "euclidean_distance" , -cell1) %>% left_join( y = dplyr::rename(anno, age_cell1 = age) %>% rownames_to_column( var = "cell1") , by = "cell1" ) %>% left_join( y = dplyr::rename(anno, age_cell2 = age) %>% rownames_to_column( var = "cell2") , by = "cell2" ) %>% mutate(comparison = paste(age_cell1 , age_cell2 , sep = "_"))
})
gg.list <- lapply( X = df.plot.list, FUN = function(x){
                                            ggplot(data = x, mapping = aes(x = euclidean_distance , color = comparison)) + geom_density() + ggtitle( paste0(unique(as.character(x$celltype.x))) )
})

qNSC1

print(gg.list[[1]])

qNSC2

print(gg.list[[2]])

aNSC0

print(gg.list[[3]])

aNSC1

print(gg.list[[4]])

aNSC2

print(gg.list[[5]])

TAP

print(gg.list[[6]])

NB

print(gg.list[[7]])

OPC

print(gg.list[[8]])

OD

print(gg.list[[9]])

Euclidean Distances between celltypes

celltype_comparisons <- list( qNSC1_qNSC2 = c("qNSC1","qNSC2") , qNSC2_aNSC0 = c("qNSC2","aNSC0") , aNSC0_aNSC1 = c("aNSC0","aNSC1") ,aNSC1_aNSC2 = c("aNSC1","aNSC2") ,aNSC2_TAP = c("aNSC2","TAP") , aNSC2_TAP = c("TAP","NB") , qNSC1_OD = c("qNSC1","OD") , qNSC1_aNSC2 = c("qNSC1","aNSC2")  )
celltype_comparisons
$qNSC1_qNSC2
[1] "qNSC1" "qNSC2"

$qNSC2_aNSC0
[1] "qNSC2" "aNSC0"

$aNSC0_aNSC1
[1] "aNSC0" "aNSC1"

$aNSC1_aNSC2
[1] "aNSC1" "aNSC2"

$aNSC2_TAP
[1] "aNSC2" "TAP"  

$aNSC2_TAP
[1] "TAP" "NB" 

$qNSC1_OD
[1] "qNSC1" "OD"   

$qNSC1_aNSC2
[1] "qNSC1" "aNSC2"

Make a matrix of the data

data_matrix_lineage <- lapply( X = celltype_comparisons, FUN = function(x){
  
                        # data_10X2_celltype <- FetchData(object = seurat_10X2 , vars.all = rownames(seurat_10X2@data) , cells.use = WhichCells(object = seurat_10X2 , ident = x) )
                        
                        data_10X2_celltype <- FetchData(object = seurat_10X2 , vars.all = c("PC1","PC2","PC3","PC4","PC5","PC6","PC7","PC8") , cells.use = WhichCells(object = seurat_10X2 , ident = x) )                      
  
                        data_10X2_celltype
                      })

Now we calculate the euclidean distance between all the cells from the comparison

euc_dist_lineage_all <- lapply( data_matrix_lineage , FUN = function(x){as.matrix(dist(x = x, method = "euclidean" , upper = TRUE))} )

Density of euclidean distances

df.plot.list_all <- lapply( X = euc_dist_lineage_all, FUN = function(x){
                                            x %>% as.data.frame() %>% rownames_to_column("cell1") %>% gather(key = "cell2" , value = "euclidean_distance" , -cell1) %>% left_join( y = dplyr::rename(anno, age_cell1 = age) %>% rownames_to_column( var = "cell1") , by = "cell1" ) %>% left_join( y = dplyr::rename(anno, age_cell2 = age) %>% rownames_to_column( var = "cell2") , by = "cell2" ) %>% mutate(comparison = paste(celltype.x , celltype.y , sep = "_"))
})
gg.list_all <- lapply( X = df.plot.list_all, FUN = function(x){
                                            ggplot(data = x, mapping = aes(x = euclidean_distance , color = comparison)) + geom_density() + ggtitle( "" )
})
print(gg.list_all[[1]])

print(gg.list_all[[2]])

print(gg.list_all[[3]])

print(gg.list_all[[4]])

print(gg.list_all[[5]])

Euclidean distances young vs old with q1_q2 as reference

q1_q2_euc_dist <- df.plot.list_all$qNSC1_qNSC2 %>% filter(comparison == "qNSC1_qNSC2")
df.plot.list_ref <- lapply( X = euc_dist_celltypes, FUN = function(x){
                                            x  %>% as.data.frame() %>% rownames_to_column("cell1") %>% gather(key = "cell2" , value = "euclidean_distance" , -cell1) %>% left_join( y = dplyr::rename(anno, age_cell1 = age) %>% rownames_to_column( var = "cell1") , by = "cell1" ) %>% left_join( y = dplyr::rename(anno, age_cell2 = age) %>% rownames_to_column( var = "cell2") , by = "cell2" ) %>% mutate(comparison = paste(age_cell1 , age_cell2 , sep = "_")) %>% dplyr::mutate(type = as.character(celltype.x) ) %>% dplyr::select( cell1,cell2,euclidean_distance,comparison , type) %>% bind_rows(q1_q2_euc_dist %>% dplyr::mutate(type = as.character(celltype.x) ) %>% dplyr::select( cell1,cell2,euclidean_distance,comparison,type)) %>% dplyr::filter(comparison %in% c("old_old","young_old","young_young","qNSC1_qNSC2")) %>% mutate(comparison = factor(comparison , levels = c("old_old","young_old","young_young","qNSC1_qNSC2")))
})
colors_comparisons <- c("old_old" = "slateblue" ,"young_old" = "deepskyblue4","young_young" = "olivedrab","qNSC1_qNSC2" = "black")
gg.list <- lapply( X = df.plot.list_ref, FUN = function(x){
                                            ggplot(data = x, mapping = aes(x = euclidean_distance , color = comparison , fill = comparison )) + geom_density( alpha = 0.3) + ggtitle( paste0(unique(as.character(x$type.x))) ) + xlab("Euclidean Distance") + ylab("Density") + scale_fill_manual(values = colors_comparisons) + scale_color_manual( values = colors_comparisons  ) + labs(color="Comparison",fill="Comparison") + ggtitle( paste0(unique(as.character(x$type))) )
})

qNSC1

print(gg.list[[1]])

qNSC2

print(gg.list[[2]])

aNSC0

print(gg.list[[3]])

aNSC1

print(gg.list[[4]])

aNSC2

print(gg.list[[5]])

TAP

print(gg.list[[6]])

NB

print(gg.list[[7]])

OPC

print(gg.list[[8]])

OD

print(gg.list[[9]])

Euclidean distances between young and old in Pseudotime window

First we subset our data to contain only the NSCs in individual tables for young and old.

data_10_NSCs <- FetchData(object = seurat_10X2 , vars.all = c("PC1","PC2","PC3","PC4","PC5","PC6","PC7","PC8") , cells.use = WhichCells( object = seurat_10X2 , ident.remove = c("OPC","OD","NB","TAP") )  )
meta_data_table <- FetchData(object = seurat_10X2 , vars.all = c("age","Pseudotime","celltype") , cells.use = WhichCells( object = seurat_10X2 , ident.remove = c("OPC","OD","NB","TAP") )  ) %>% rownames_to_column("cell")
eucl_distances_all_NSCs <- as.matrix( dist(data_10_NSCs , method = "euclidean" , upper = TRUE) )
pseudotime_diff = 2.5

data_euclidean_distances <- lapply( X = rownames(data_10_NSCs)  , FUN = function(x){
    require(dplyr)
  
    pseudotime_val <- meta_data_table %>% filter( cell == x  ) %>% pull("Pseudotime")
  
    age_cell <- meta_data_table %>% filter( cell == x  ) %>% pull("age")
  
    pseudotime_vals <- c( min = pseudotime_val - pseudotime_diff , value = pseudotime_val  , max = pseudotime_val + pseudotime_diff ) 
    
    cells2compare <- meta_data_table %>% filter( Pseudotime < pseudotime_vals["max"]  &  Pseudotime > pseudotime_vals["min"] ) 
    
    cells2compare_young <- cells2compare %>% filter( age == "young" & cell != x ) %>% pull("cell") 

    cells2compare_old <- cells2compare %>% filter( age == "old" & cell != x ) %>% pull("cell")
        
  if(age_cell == "young"){
    young_to_young <- data.frame( euclidean_distance = as.numeric(eucl_distances_all_NSCs[ cells2compare_young , x ]) , comparison = as.character("young_to_young") , Pseudotime = pseudotime_val ) 
    
    young_to_old <- data.frame( euclidean_distance = as.numeric(eucl_distances_all_NSCs[ cells2compare_old , x ]) , comparison = as.character("young_to_old") , Pseudotime = pseudotime_val )
    
    young_to_young$comparison <- as.character(young_to_young$comparison)
    young_to_old$comparison <- as.character(young_to_old$comparison)
    
    data_comparisons <- bind_rows( young_to_young , young_to_old )
    
  }else{
    data_comparisons <- data.frame( euclidean_distance = as.numeric(eucl_distances_all_NSCs[ cells2compare_old , x ]) , comparison = as.character("old_to_old") , Pseudotime = pseudotime_val )
    
    data_comparisons$comparison <- as.character(data_comparisons$comparison)
    
  }
    
  data_comparisons
})
data_euclidean_distances_df <- bind_rows( data_euclidean_distances )
qNSC1_cells <- WhichCells( object = seurat_10X2 , ident = "qNSC1")
qNSC2_cells <- WhichCells( object = seurat_10X2 , ident = "qNSC2")
# data_euclidean_distances_df_all_NSCs <- eucl_distances_all_NSCs[qNSC1_cells,qNSC2_cells] %>% as.data.frame() %>% gather(key = "cell" , value = "euclidean_distance" )
# data_euclidean_distances_df_all_NSCs <- left_join(x = data_euclidean_distances_df_all_NSCs , y = meta_data_table[,c("Pseudotime","cell")] )
# data_euclidean_distances_df_all_NSCs$comparison <- rep("qNSC1_vs_qNSC2")
## Discard the comparisons of the same cells
#data_euclidean_distances_df_all_NSCs <- data_euclidean_distances_df_all_NSCs %>% filter( euclidean_distance != 0 )
pseudotime_diff = 2.5
data_euclidean_distances_q1_q2_pseudotimewindow <- lapply( X = c(qNSC1_cells)  , FUN = function(x){
    require(dplyr)
  
    pseudotime_val <- meta_data_table %>% filter( cell == x  ) %>% pull("Pseudotime")
  
    type_cell <- meta_data_table %>% filter( cell == x  ) %>% pull("celltype")
  
    pseudotime_vals <- c( min = pseudotime_val - pseudotime_diff , value = pseudotime_val  , max = pseudotime_val + pseudotime_diff ) 
    
    cells2compare <- meta_data_table %>% filter( Pseudotime < pseudotime_vals["max"]  &  Pseudotime > pseudotime_vals["min"] ) 
    
    cells2compare_q1 <- cells2compare %>% filter( celltype == "qNSC1" & cell != x ) %>% pull("cell") 
    cells2compare_q2 <- cells2compare %>% filter( celltype == "qNSC2" & cell != x ) %>% pull("cell")
        
  if(type_cell == "qNSC1"){
   
    q1_to_q2 <- data.frame( euclidean_distance = as.numeric(eucl_distances_all_NSCs[ cells2compare_q2 , x ]) , comparison = as.character("qNSC1_vs_qNSC2") , Pseudotime = pseudotime_val )
    
    q1_to_q2$comparison <- as.character(q1_to_q2$comparison)
    
    data_comparisons <- q1_to_q2
  }else{
    stop("Error")
  }
    
  data_comparisons
})
data_euclidean_distances_df_q1_q2 <- bind_rows( data_euclidean_distances_q1_q2_pseudotimewindow )

Merge the age comparisons with the all vs all comparison

data_euclidean_distances_combined_df <- bind_rows( data_euclidean_distances_df , data_euclidean_distances_df_q1_q2  )
data_euclidean_distances_combined_df <-  data_euclidean_distances_combined_df %>% group_by(comparison) %>% mutate( mean_euclidean_distance = mean(euclidean_distance) )

Density of euclidean distances

comparison_colors <- c("old_to_old" = "slateblue" ,"young_to_old" = "deepskyblue4","young_to_young" = "olivedrab","qNSC1_vs_qNSC2" = "black")
data_euclidean_distances_combined_df$comparison <- factor( data_euclidean_distances_combined_df$comparison , levels = c("qNSC1_vs_qNSC2" , "young_to_young" , "young_to_old" ,  "old_to_old" ) )
gg_pseudotimewindow <- ggplot(data = data_euclidean_distances_combined_df , mapping = aes(x = euclidean_distance , color = comparison , fill = comparison )) + geom_density(alpha = 0.5) +  ggtitle( "Euclidean distances" )  + geom_vline(mapping = aes(xintercept = mean_euclidean_distance ) , color = "grey20" ) + facet_grid(facets = comparison ~ .) + scale_fill_manual(values =  comparison_colors , labels = c("qNSC1 to qNSC2" , "young to young" , "young to old" ,  "old to old" ) , name = "Comparison") + scale_color_manual(values =  comparison_colors , labels = c("qNSC1 to qNSC2" , "young to young" , "young to old" ,  "old to old" ) , name = "Comparison" ) + theme(strip.text.y = element_text(size = 8))
gg_pseudotimewindow

End

sessionInfo()
R version 3.4.3 (2017-11-30)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.3 LTS

Matrix products: default
BLAS: /usr/lib/libblas/libblas.so.3.6.0
LAPACK: /usr/lib/lapack/liblapack.so.3.6.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats4    parallel  stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] genefilter_1.60.0          gridExtra_2.2.1            ggrepel_0.8.0              org.Mm.eg.db_3.5.0        
 [5] AnnotationDbi_1.40.0       clusterProfiler_3.4.4      DOSE_3.4.0                 bindrcpp_0.2              
 [9] viridis_0.4.1              viridisLite_0.3.0          knitr_1.20                 BiocParallel_1.12.0       
[13] DESeq2_1.16.1              SummarizedExperiment_1.8.1 DelayedArray_0.4.1         matrixStats_0.53.1        
[17] GenomicRanges_1.30.3       GenomeInfoDb_1.14.0        IRanges_2.12.0             S4Vectors_0.16.0          
[21] Seurat_2.2.0               Matrix_1.2-14              cowplot_0.7.0              forcats_0.3.0             
[25] stringr_1.3.1              dplyr_0.7.4                purrr_0.2.4                readr_1.1.1               
[29] tidyr_0.7.2                tibble_1.4.2               ggplot2_2.2.1              tidyverse_1.2.1           
[33] Biobase_2.38.0             BiocGenerics_0.24.0       

loaded via a namespace (and not attached):
  [1] R.utils_2.6.0          lme4_1.1-18-1          RSQLite_2.1.1          htmlwidgets_1.2        grid_3.4.3            
  [6] trimcluster_0.1-2      ranger_0.6.0           Rtsne_0.11             munsell_0.4.3          codetools_0.2-15      
 [11] ica_1.0-2              colorspace_1.3-2       GOSemSim_2.4.1         rstudioapi_0.7         ROCR_1.0-7            
 [16] robustbase_0.92-7      dtw_1.20-1             NMF_0.20.6             lars_1.2               GenomeInfoDbData_1.0.0
 [21] mnormt_1.5-5           bit64_0.9-7            diptest_0.75-7         R6_2.2.2               doParallel_1.0.10     
 [26] VGAM_1.0-3             locfit_1.5-9.1         flexmix_2.3-13         bitops_1.0-6           fgsea_1.4.1           
 [31] assertthat_0.2.0       SDMTools_1.1-221       scales_0.5.0           nnet_7.3-12            gtable_0.2.0          
 [36] rlang_0.2.2            MatrixModels_0.4-1     scatterplot3d_0.3-41   splines_3.4.3          lazyeval_0.2.0        
 [41] ModelMetrics_1.1.0     acepack_1.4.1          broom_0.4.3            checkmate_1.8.5        yaml_2.2.0            
 [46] reshape2_1.4.2         abind_1.4-5            modelr_0.1.1           backports_1.1.2        qvalue_2.10.0         
 [51] Hmisc_4.1-1            caret_6.0-73           tools_3.4.3            psych_1.7.8            gridBase_0.4-7        
 [56] gplots_3.0.1           RColorBrewer_1.1-2     proxy_0.4-22           ggridges_0.5.0         Rcpp_0.12.18          
 [61] plyr_1.8.4             base64enc_0.1-3        zlibbioc_1.24.0        RCurl_1.95-4.10        rpart_4.1-12          
 [66] pbapply_1.3-1          haven_1.1.2            cluster_2.0.6          magrittr_1.5           data.table_1.11.6     
 [71] DO.db_2.9              openxlsx_4.1.0         mvtnorm_1.0-5          hms_0.4.2              xtable_1.8-2          
 [76] XML_3.98-1.11          rio_0.5.10             mclust_5.2.2           readxl_1.1.0           compiler_3.4.3        
 [81] KernSmooth_2.23-15     crayon_1.3.4           minqa_1.2.4            R.oo_1.22.0            htmltools_0.3.6       
 [86] segmented_0.5-1.4      Formula_1.2-3          geneplotter_1.56.0     tclust_1.2-3           lubridate_1.7.1       
 [91] DBI_1.0.0              diffusionMap_1.1-0.1   MASS_7.3-48            fpc_2.1-10             boot_1.3-20           
 [96] car_3.0-2              cli_1.0.0              R.methodsS3_1.7.1      gdata_2.17.0           bindr_0.1             
[101] igraph_1.0.1           pkgconfig_2.0.2        sn_1.5-0               rvcheck_0.1.0          registry_0.3          
[106] numDeriv_2016.8-1      foreign_0.8-69         xml2_1.1.1             foreach_1.4.3          annotate_1.56.2       
[111] rngtools_1.2.4         pkgmaker_0.22          XVector_0.18.0         rvest_0.3.2            digest_0.6.12         
[116] tsne_0.1-3             cellranger_1.1.0       fastmatch_1.1-0        htmlTable_1.12         curl_3.2              
[121] kernlab_0.9-25         gtools_3.5.0           modeltools_0.2-22      nloptr_1.0.4           nlme_3.1-131          
[126] jsonlite_1.5           carData_3.0-1          pillar_1.3.0           lattice_0.20-35        httr_1.3.1            
[131] DEoptimR_1.0-8         survival_2.41-3        GO.db_3.5.0            glue_1.3.0             zip_1.0.0             
[136] FNN_1.1                prabclus_2.2-6         iterators_1.0.8        bit_1.1-14             class_7.3-14          
[141] stringi_1.2.4          mixtools_1.0.4         blob_1.1.1             latticeExtra_0.6-28    caTools_1.17.1        
[146] memoise_1.0.0          irlba_2.1.2            ape_5.1               
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgTmV1cmFsIFN0ZW0gQ2VsbHMgZnJvbSB0aGUgc3VidmVudHJpY3VsYXIgem9uZSBzZXF1ZW5jZWQgYnkgMTBYIEdlbm9taWNzIDMnIENocm9taXVtIHByb3RvY29sIChwYXJ0IDIpIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgc21hcnQ6IG5vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCiMgUHJlcGFyYXRpb25zCgojIyBMb2FkaW5nIHRoZSBuZWNjZXNzYXJ5IHBhY2thZ2VzCgpgYGB7cn0KbGlicmFyeSgidGlkeXZlcnNlIikKbGlicmFyeSgiU2V1cmF0IikKbGlicmFyeSgiTWF0cml4IikKCmxpYnJhcnkoIkRFU2VxMiIpCmxpYnJhcnkoIkJpb2NQYXJhbGxlbCIpCnJlZ2lzdGVyKE11bHRpY29yZVBhcmFtKDQpKQoKbGlicmFyeSgia25pdHIiKQpsaWJyYXJ5KCJ2aXJpZGlzIikKbGlicmFyeSgiZ3JpZEV4dHJhIikKYGBgCgpgYGB7cn0Kc2F2ZV9jc3YgPC0gRkFMU0UKYGBgCgojIyBMb2FkaW5nIHRoZSBkYXRhCgpgYGB7cn0Kc2V1cmF0XzEwWDIgPC0gcmVhZFJEUyhmaWxlID0gInNldXJhdF8xMFgyX2NsdXN0ZXJlZF9taW5fMTUwMF9uR2VuZS5SRFMiKQoKVFNORVBsb3Qoc2V1cmF0XzEwWDIgLCBkby5sYWJlbCA9IFRSVUUpCmBgYAoKIyMgU2V0IHRoZSBpZGVudGl0eSB0byBjZWxsdHlwZSBhbmQgYWdlIGNvbWJpbmF0aW9uCgpgYGB7cn0Kc2V1cmF0XzEwWDJAbWV0YS5kYXRhWywiY2VsbHR5cGVfYWdlIl0gPC0gcGFzdGUoIHNldXJhdF8xMFgyQG1ldGEuZGF0YSRjZWxsdHlwZSAsIHNldXJhdF8xMFgyQG1ldGEuZGF0YSRhZ2UgLCBzZXAgPSAiXyIgKQpgYGAKCiMgRGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGJldHdlZW4gY2VsbHMgZnJvbSBvbGQgYW5kIHlvdW5nIG1pY2UKCiMjIERFU2VxMgoKIyMjIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIGJldHdlZW4gTlNDcyBmcm9tIG9sZCB2cyB5b3VuZyBhbmltYWxzIHdpdGggREVTZXEyCgpgYGB7cn0Kc2V1cmF0XzEwWDIgPC0gU2V0QWxsSWRlbnQob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZCA9ICJjZWxsdHlwZV9hZ2UiKQoKc2V1cmF0XzEwWDIgPC0gU2V0SWRlbnQob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBjZWxscy51c2UgPSBXaGljaENlbGxzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQgPSBjKCJxTlNDMV9vbGQiLCJxTlNDMl9vbGQiLCJhTlNDMF9vbGQiLCJhTlNDMV9vbGQiLCJhTlNDMl9vbGQiKSApICwgaWRlbnQudXNlID0gIk5TQ19vbGQiICApCgpzZXVyYXRfMTBYMiA8LSBTZXRJZGVudChvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGNlbGxzLnVzZSA9IFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9IGMoInFOU0MxX3lvdW5nIiwicU5TQzJfeW91bmciLCJhTlNDMF95b3VuZyIsImFOU0MxX3lvdW5nIiwiYU5TQzJfeW91bmciICkgKSAsIGlkZW50LnVzZSA9ICJOU0NfeW91bmciICApCgpUU05FUGxvdChvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGRvLmxhYmVsID0gVFJVRSApCgojIFJ1biBERVNlcTIKREVfb2xkX3ZzX3lvdW5nX0RFU2VxMiA8LSBGaW5kTWFya2VycyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LjEgPSAiTlNDX29sZCIgLCBpZGVudC4yID0gIk5TQ195b3VuZyIgLCB0ZXN0LnVzZSA9ICJERVNlcTIiICwgcGFyYWxsZWwgPSBUUlVFICwgbWluLnBjdCA9IDAgLCBsb2dmYy50aHJlc2hvbGQgPSAwICkKCkRFX29sZF92c195b3VuZ19ERVNlcTIgPC0gREVfb2xkX3ZzX3lvdW5nX0RFU2VxMiAlPiUgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImdlbmVfc3ltYm9sIikgJT4lIG11dGF0ZSggYXZnX2xvZ0ZDID0gYXZnX2xvZ0ZDL2xvZygyKSApICU+JSBkcGx5cjo6cmVuYW1lKCBhdmdfbG9nMkZDID0gYXZnX2xvZ0ZDICkKCmlmKHNhdmVfY3N2KXsKCiAgd3JpdGUuY3N2KCB4ID0gREVfb2xkX3ZzX3lvdW5nX0RFU2VxMiAsIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvREVfREVTZXEyX29sZF92c195b3VuZy9ERV9nZW5lc19OU0NfbG9nMkZDX29sZF92c195b3VuZ18xMFhfMl9ERVNlcTJfZnJvbV9TZXVyYXQuY3N2IiApICkKCn0KYGBgCgojIyMgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gYmV0d2VlbiBxTlNDcyBmb3Igb2xkIHZzIHlvdW5nIGFuaW1hbHMgYW5kIGFOU0MgZm9yIG9sZCB2cyB5b3VuZyBhbmltYWxzIHdpdGggREVTZXEyCgpgYGB7cn0KIHNldXJhdF8xMFgyIDwtIFNldEFsbElkZW50KG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWQgPSAiY2VsbHR5cGVfYWdlIikKCiBzZXVyYXRfMTBYMiA8LSBTZXRJZGVudChvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGNlbGxzLnVzZSA9IFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9IGMoInFOU0MxX29sZCIsInFOU0MyX29sZCIpICkgLCBpZGVudC51c2UgPSAicU5TQ19vbGQiICApCgogc2V1cmF0XzEwWDIgPC0gU2V0SWRlbnQob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBjZWxscy51c2UgPSBXaGljaENlbGxzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQgPSBjKCJhTlNDMF9vbGQiLCJhTlNDMV9vbGQiLCJhTlNDMl9vbGQiKSApICwgaWRlbnQudXNlID0gImFOU0Nfb2xkIiAgKQoKIHNldXJhdF8xMFgyIDwtIFNldElkZW50KG9iamVjdCA9IHNldXJhdF8xMFgyICwgY2VsbHMudXNlID0gV2hpY2hDZWxscyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gYygicU5TQzFfeW91bmciLCJxTlNDMl95b3VuZyIpICkgLCBpZGVudC51c2UgPSAicU5TQ195b3VuZyIgICkKCiBzZXVyYXRfMTBYMiA8LSBTZXRJZGVudChvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGNlbGxzLnVzZSA9IFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9IGMoImFOU0MwX3lvdW5nIiwiYU5TQzFfeW91bmciLCJhTlNDMl95b3VuZyIpICkgLCBpZGVudC51c2UgPSAiYU5TQ195b3VuZyIgICkKCiBUU05FUGxvdChvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGRvLmxhYmVsID0gVFJVRSApCgojIFJ1biBERVNlcTIKCkRFX3FOU0Nfb2xkX3ZzX3lvdW5nX0RFU2VxMiA8LSBGaW5kTWFya2VycyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LjEgPSAicU5TQ19vbGQiICwgaWRlbnQuMiA9ICJxTlNDX3lvdW5nIiAsIHRlc3QudXNlID0gIkRFU2VxMiIgLCBwYXJhbGxlbCA9IFRSVUUgLCBtaW4ucGN0ID0gMCAsIGxvZ2ZjLnRocmVzaG9sZCA9IDApCgpERV9xTlNDX29sZF92c195b3VuZ19ERVNlcTIgPC0gREVfcU5TQ19vbGRfdnNfeW91bmdfREVTZXEyICU+JSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9zeW1ib2wiKSAlPiUgbXV0YXRlKCBhdmdfbG9nRkMgPSBhdmdfbG9nRkMvbG9nKDIpICkgJT4lIGRwbHlyOjpyZW5hbWUoIGF2Z19sb2cyRkMgPSBhdmdfbG9nRkMgKQoKCkRFX2FOU0Nfb2xkX3ZzX3lvdW5nX0RFU2VxMiA8LSBGaW5kTWFya2VycyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LjEgPSAiYU5TQ19vbGQiICwgaWRlbnQuMiA9ICJhTlNDX3lvdW5nIiAsIHRlc3QudXNlID0gIkRFU2VxMiIgLCBwYXJhbGxlbCA9IFRSVUUgLCBtaW4ucGN0ID0gMCAsIGxvZ2ZjLnRocmVzaG9sZCA9IDApCgpERV9hTlNDX29sZF92c195b3VuZ19ERVNlcTIgPC0gREVfYU5TQ19vbGRfdnNfeW91bmdfREVTZXEyICU+JSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9zeW1ib2wiKSAlPiUgbXV0YXRlKCBhdmdfbG9nRkMgPSBhdmdfbG9nRkMvbG9nKDIpICkgJT4lIGRwbHlyOjpyZW5hbWUoIGF2Z19sb2cyRkMgPSBhdmdfbG9nRkMgKQoKaWYoc2F2ZV9jc3YpewogIHdyaXRlLmNzdiggeCA9IERFX2FOU0Nfb2xkX3ZzX3lvdW5nX0RFU2VxMiAsIGZpbGUgPSAicmVzdWx0cy9ERV9ERVNlcTJfb2xkX3ZzX3lvdW5nL0RFX2dlbmVzX2FOU0NfbG9nMkZDX29sZF92c195b3VuZ18xMFhfMl9ERVNlcTJfZnJvbV9TZXVyYXQuY3N2IiApIAogIHdyaXRlLmNzdiggeCA9IERFX3FOU0Nfb2xkX3ZzX3lvdW5nX0RFU2VxMiAsIGZpbGUgPSAicmVzdWx0cy9ERV9ERVNlcTJfb2xkX3ZzX3lvdW5nL0RFX2dlbmVzX3FOU0NfbG9nMkZDX29sZF92c195b3VuZ18xMFhfMl9ERVNlcTJfZnJvbV9TZXVyYXQuY3N2IiApIAp9CgpgYGAKCiMjIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBiZXR3ZWVuIHlvdW5nIGFuZCBvbGQgZm9yIGFsbCBjZWxsdHlwZXMgaW5kaXZpZHVhbGx5CgpgYGB7cn0Kc2V1cmF0XzEwWDIgPC0gU2V0QWxsSWRlbnQoIG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWQgPSAiY2VsbHR5cGVfYWdlIiApCgpUU05FUGxvdChvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGRvLmxhYmVsID0gVFJVRSApCmBgYAoKRGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cyBhcmUgYWx3YXlzIGNvbXBhcmVkIG9sZCBvdmVyIHlvdW5nIG1lYW5pbmcgdGhhdCBpZiBpbiB0aGUgcmVzdWx0cyB0aGVyZSBpcyBhIGxvZzJGQyBvZiAxLCB0aGUgZ2VuZSBpcyBsb2cyKDEpIGZvbGQgaGlnaGVyIGluIHRoZSBvbGQgdGhhbiBpbiB0aGUgeW91bmcKClVzZSBERVNlcTIgZm9yIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIHRlc3Rpbmcgb2YgdGhlIGluZGl2aWR1YWwgc3VicG9wdWxhdGlvbnMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjIHF1aWVzY2VudCBuZXVyb25hbCBzdGVtIGNlbGxzCiBtYXJrZXJzX3FOU0MxX29sZF92c195b3VuZ19ERVNlcTIgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC4xID0gInFOU0MxX29sZCIgLCBpZGVudC4yID0gInFOU0MxX3lvdW5nIiAsIHRlc3QudXNlID0gIkRFU2VxMiIgLCBwYXJhbGxlbCA9IFRSVUUgLCBwcmludC5iYXIgPSBGQUxTRSAgLCBtaW4ucGN0ID0gMCAsIGxvZ2ZjLnRocmVzaG9sZCA9IDApCiMjIHF1aWVzY2VudCBuZXVyb25hbCBzdGVtIGNlbGxzIChwcmltZWQpCiBtYXJrZXJzX3FOU0MyX29sZF92c195b3VuZ19ERVNlcTIgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC4xID0gInFOU0MyX29sZCIgLCBpZGVudC4yID0gInFOU0MyX3lvdW5nIiAsIHRlc3QudXNlID0gIkRFU2VxMiIgLCBwYXJhbGxlbCA9IFRSVUUgLCBwcmludC5iYXIgPSBGQUxTRSAsIG1pbi5wY3QgPSAwICwgbG9nZmMudGhyZXNob2xkID0gMCkKIyMgYWN0aXZlIG5ldXJvbmFsIHN0ZW0gY2VsbHMKIG1hcmtlcnNfYU5TQzBfb2xkX3ZzX3lvdW5nX0RFU2VxMiA8LSBGaW5kTWFya2VycyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LjEgPSAiYU5TQzBfb2xkIiAsIGlkZW50LjIgPSAiYU5TQzBfeW91bmciICwgdGVzdC51c2UgPSAiREVTZXEyIiAsIHBhcmFsbGVsID0gVFJVRSAsIHByaW50LmJhciA9IEZBTFNFICwgbWluLnBjdCA9IDAgLCBsb2dmYy50aHJlc2hvbGQgPSAwKQogbWFya2Vyc19hTlNDMV9vbGRfdnNfeW91bmdfREVTZXEyIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQuMSA9ICJhTlNDMV9vbGQiICwgaWRlbnQuMiA9ICJhTlNDMV95b3VuZyIgLCB0ZXN0LnVzZSA9ICJERVNlcTIiICwgcGFyYWxsZWwgPSBUUlVFICwgcHJpbnQuYmFyID0gRkFMU0UgLCBtaW4ucGN0ID0gMCAsIGxvZ2ZjLnRocmVzaG9sZCA9IDApCiBtYXJrZXJzX2FOU0MyX29sZF92c195b3VuZ19ERVNlcTIgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC4xID0gImFOU0MyX29sZCIgLCBpZGVudC4yID0gImFOU0MyX3lvdW5nIiAsIHRlc3QudXNlID0gIkRFU2VxMiIgLCBwYXJhbGxlbCA9IFRSVUUgLCBwcmludC5iYXIgPSBGQUxTRSAsIG1pbi5wY3QgPSAwICwgbG9nZmMudGhyZXNob2xkID0gMCkKIyMgVEFQcyBhbmQgTkIKbWFya2Vyc19UQVBfb2xkX3ZzX3lvdW5nX0RFU2VxMiA8LSBGaW5kTWFya2VycyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LjEgPSAiVEFQX29sZCIgLCBpZGVudC4yID0gIlRBUF95b3VuZyIgLCB0ZXN0LnVzZSA9ICJERVNlcTIiICwgcGFyYWxsZWwgPSBUUlVFICwgcHJpbnQuYmFyID0gRkFMU0UgLCBtaW4ucGN0ID0gMCAsIGxvZ2ZjLnRocmVzaG9sZCA9IDApCiBtYXJrZXJzX05CX29sZF92c195b3VuZ19ERVNlcTIgPC0gRmluZE1hcmtlcnMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC4xID0gIk5CX29sZCIgLCBpZGVudC4yID0gIk5CX3lvdW5nIiAsIHRlc3QudXNlID0gIkRFU2VxMiIgLCBwYXJhbGxlbCA9IFRSVUUgLCBwcmludC5iYXIgPSBGQUxTRSAsIG1pbi5wY3QgPSAwICwgbG9nZmMudGhyZXNob2xkID0gMCkKCiMgT0QgYW5kIE9QQwptYXJrZXJzX09QQ19vbGRfdnNfeW91bmdfREVTZXEyIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQuMSA9ICJPUENfb2xkIiAsIGlkZW50LjIgPSAiT1BDX3lvdW5nIiAsIHRlc3QudXNlID0gIkRFU2VxMiIgLCBwYXJhbGxlbCA9IFRSVUUgLCBwcmludC5iYXIgPSBGQUxTRSAsIG1pbi5wY3QgPSAwICwgbG9nZmMudGhyZXNob2xkID0gMCkKbWFya2Vyc19PRF9vbGRfdnNfeW91bmdfREVTZXEyIDwtIEZpbmRNYXJrZXJzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQuMSA9ICJPRF9vbGQiICwgaWRlbnQuMiA9ICJPRF95b3VuZyIgLCB0ZXN0LnVzZSA9ICJERVNlcTIiICwgcGFyYWxsZWwgPSBUUlVFICwgcHJpbnQuYmFyID0gRkFMU0UgLCBtaW4ucGN0ID0gMCAsIGxvZ2ZjLnRocmVzaG9sZCA9IDApCgppZihzYXZlX2Nzdil7CiAgCiBERV9kYXRhX2xpc3QgPC0gbGlzdCggImdlbmVzX3FOU0MxX2xvZzJGQ19vbGRfdnNfeW91bmdfMTBYXzJfREVTZXEyX2Zyb21fU2V1cmF0IiA9IG1hcmtlcnNfcU5TQzFfb2xkX3ZzX3lvdW5nX0RFU2VxMiAsICJnZW5lc19xTlNDMl9sb2cyRkNfb2xkX3ZzX3lvdW5nXzEwWF8yX0RFU2VxMl9mcm9tX1NldXJhdCIgPSBtYXJrZXJzX3FOU0MyX29sZF92c195b3VuZ19ERVNlcTIgLCAiZ2VuZXNfYU5TQzBfbG9nMkZDX29sZF92c195b3VuZ18xMFhfMl9ERVNlcTJfZnJvbV9TZXVyYXQiID0gbWFya2Vyc19hTlNDMF9vbGRfdnNfeW91bmdfREVTZXEyICwgImdlbmVzX2FOU0MxX2xvZzJGQ19vbGRfdnNfeW91bmdfMTBYXzJfREVTZXEyX2Zyb21fU2V1cmF0IiA9IG1hcmtlcnNfYU5TQzFfb2xkX3ZzX3lvdW5nX0RFU2VxMiAsICJnZW5lc19hTlNDMl9sb2cyRkNfb2xkX3ZzX3lvdW5nXzEwWF8yX0RFU2VxMl9mcm9tX1NldXJhdCIgPSBtYXJrZXJzX2FOU0MyX29sZF92c195b3VuZ19ERVNlcTIsICJnZW5lc19UQVBfbG9nMkZDX29sZF92c195b3VuZ18xMFhfMl9ERVNlcTJfZnJvbV9TZXVyYXQiID0gbWFya2Vyc19UQVBfb2xkX3ZzX3lvdW5nX0RFU2VxMiAsICJnZW5lc19OQl9sb2cyRkNfb2xkX3ZzX3lvdW5nXzEwWF8yX0RFU2VxMl9mcm9tX1NldXJhdCIgPSBtYXJrZXJzX05CX29sZF92c195b3VuZ19ERVNlcTIgLCAiZ2VuZXNfT1BDX2xvZzJGQ19vbGRfdnNfeW91bmdfMTBYXzJfREVTZXEyX2Zyb21fU2V1cmF0IiA9IG1hcmtlcnNfT1BDX29sZF92c195b3VuZ19ERVNlcTIgLCAiZ2VuZXNfT0RfbG9nMkZDX29sZF92c195b3VuZ18xMFhfMl9ERVNlcTJfZnJvbV9TZXVyYXQiID0gbWFya2Vyc19PRF9vbGRfdnNfeW91bmdfREVTZXEyICkKICAKIERFX2RhdGFfbGlzdCA8LSBsYXBwbHkoIERFX2RhdGFfbGlzdCwgRlVOID0gZnVuY3Rpb24oeCl7CiAgIHggJT4lIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX3N5bWJvbCIpICU+JSBtdXRhdGUoIGF2Z19sb2dGQyA9IGF2Z19sb2dGQy9sb2coMikgKSAlPiUgZHBseXI6OnJlbmFtZSggYXZnX2xvZzJGQyA9IGF2Z19sb2dGQyApCiB9KQogCiBpZihzYXZlX2Nzdil7CiAgIGZvciggY3QgaW4gc2VxKDEsbGVuZ3RoKERFX2RhdGFfbGlzdCkpICl7CiAgICAgIHdyaXRlLmNzdih4ID0gREVfZGF0YV9saXN0W1tjdF1dICwgZmlsZSA9IHBhc3RlMCggInJlc3VsdHMvREVfREVTZXEyX29sZF92c195b3VuZy9jZWxsdHlwZXNfaW5kaXZpZHVhbF9vbGRfdnNfeW91bmcvREVfIiAsIG5hbWVzKERFX2RhdGFfbGlzdFtjdF0pICwgIi5jc3YiICkgKSAgCiAgIH0KIH0KfQpgYGAKCgojIyB0LXRlc3QKCmBgYHtyfQpsaWJyYXJ5KGdlbmVmaWx0ZXIpCgp0ZW54X2RhdGEgPC0gc2V1cmF0XzEwWDJAZGF0YQpUUE1fTlNDIDwtIGFzLm1hdHJpeCh0ZW54X2RhdGEpCgojIExvYWQgdGhlIGNlbGwgYW5ub3RhdGlvbgp0ZW54X2Fubm90YXRpb24gPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgdmFycy5hbGwgPSBjKCJjZWxsdHlwZSIsImFnZSIpICkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JSBkcGx5cjo6cmVuYW1lKHR5cGUgPSBjZWxsdHlwZSkKdGVueF9hbm5vdGF0aW9uJHR5cGUgPC0gZmFjdG9yKHggPSB0ZW54X2Fubm90YXRpb24kdHlwZSAsIGxldmVscyA9IGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMCIsImFOU0MxIiwiYU5TQzIiLCJUQVAiLCJOQiIsIk9QQyIsIk9EIikpCnRlbnhfYW5ub3RhdGlvbiRhZ2UgPC0gZmFjdG9yKHggPSB0ZW54X2Fubm90YXRpb24kYWdlICwgbGV2ZWxzID0gYygieW91bmciLCJvbGQiKSkKTlNDX2Fubm8gPC0gdGVueF9hbm5vdGF0aW9uCgojIFN1YnNldCB0aGUgZXhwcmVzc2lvbiBtYXRyaXggZm9yIHlvdW5nIGFuZCBvbGQgaW50byBkaWZmZXJlbnQgbWF0cmljZXMKVFBNX29sZCA8LSBUUE1fTlNDWyxhcy5jaGFyYWN0ZXIoTlNDX2Fubm9bTlNDX2Fubm8kYWdlID09ICJvbGQiLF0kY2VsbCldClRQTV95b3VuZyA8LSBUUE1fTlNDWyxhcy5jaGFyYWN0ZXIoTlNDX2Fubm9bTlNDX2Fubm8kYWdlID09ICJ5b3VuZyIsXSRjZWxsKV0KCk5TQ19hbm5vX3lvdW5nIDwtIE5TQ19hbm5vW05TQ19hbm5vJGFnZSA9PSAieW91bmciLF0KTlNDX2Fubm9fb2xkIDwtIE5TQ19hbm5vW05TQ19hbm5vJGFnZSA9PSAib2xkIixdCgojIEV4cHJlc3Npb24gdmFsdWVzIGFyZSBhbHJlYWR5IGxvZyB0cmFuc2Zvcm1lZApgYGAKCkRlZmluZSBmdW5jdGlvbiB0byBydW4gdC10ZXN0CgpgYGB7cn0KcnVuX3R0ZXN0X2FuZF9jYWxjdWxhdGVfQ29oZW5zX2QgPC0gZnVuY3Rpb24oc3VicG9wKXsKICAKICAgICMjIFN1YnNldCBjZWxscyBmb3IgaW5kaXZpZHVhbCBzdWJwb3B1bGF0aW9ucwogICAgVFBNX3lvdW5nX3N1YnBvcCA8LSBUUE1feW91bmdbLGFzLmNoYXJhY3RlcihOU0NfYW5ub195b3VuZ1tOU0NfYW5ub195b3VuZyR0eXBlID09IHN1YnBvcCxdJGNlbGwpXQogICAgVFBNX29sZF9zdWJwb3AgPC0gVFBNX29sZFssYXMuY2hhcmFjdGVyKE5TQ19hbm5vX29sZFtOU0NfYW5ub19vbGQkdHlwZSA9PSBzdWJwb3AsXSRjZWxsKV0KICAgIFRQTV9OU0Nfc3VicG9wIDwtIFRQTV9OU0NbLGFzLmNoYXJhY3RlcihOU0NfYW5ub1tOU0NfYW5ubyR0eXBlID09IHN1YnBvcCxdJGNlbGwpXQogIAogICAgIyBDYWxjdWxhdGUgdGhlIG1lYW4gZXhwcmVzc2lvbiwgc3RhbmRhcmQgZGV2aWF0aW9uIGFuZCBudW1iZXIgb2YgY2VsbHMgd2l0aCAwIGNvdW50cyBmb3IgZWFjaCByb3cgKGdlbmUpIGZvciB5b3VuZyBhbmQgb2xkIGNlbGxzIHNlcGFyYXRlbHkKICAgIHRhYmxlXzEwWCA8LSBkYXRhLmZyYW1lKCAKICAgICAgZ2VuZV9pZCA9IHJvd25hbWVzKFRQTV95b3VuZ19zdWJwb3ApLAogICAgICBhdmdfbG9nVFBNX3lvdW5nID0gYXBwbHkoVFBNX3lvdW5nX3N1YnBvcCAsIE1BUkdJTiA9IDEgLCBGVU4gPSBtZWFuKSAgLCAKICAgICAgYXZnX2xvZ1RQTV9vbGQgPSBhcHBseShUUE1fb2xkX3N1YnBvcCAsIE1BUkdJTiA9IDEgLCBGVU4gPSBtZWFuKSAgLAogICAgICBzZF9sb2dUUE1feW91bmcgPSBhcHBseShUUE1feW91bmdfc3VicG9wICwgTUFSR0lOID0gMSAsIEZVTiA9IHNkKSAgLAogICAgICBzZF9sb2dUUE1fb2xkID0gYXBwbHkoVFBNX29sZF9zdWJwb3AgLCBNQVJHSU4gPSAxICwgRlVOID0gc2QpLAogICAgICBwZXJjZW50X2NlbGxzX3plcm9feW91bmcgPSBhcHBseShUUE1feW91bmdfc3VicG9wID09IDAgLCBNQVJHSU4gPSAxICwgRlVOID0gc3VtKS9uY29sKFRQTV95b3VuZ19zdWJwb3ApLAogICAgICBwZXJjZW50X2NlbGxzX3plcm9fb2xkID0gYXBwbHkoVFBNX29sZF9zdWJwb3AgPT0gMCAsIE1BUkdJTiA9IDEgLCBGVU4gPSBzdW0pL25jb2woVFBNX29sZF9zdWJwb3ApCiAgICApCiAgICAKICAgICMgQ2FsY3VsYXRlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lYW4gZnJvbSBvbGQgYW5kIHlvdW5nIChvbGQgLSB5b3VuZykKICAgIHRhYmxlXzEwWCRkaWZmZXJlbmNlX29mX2F2ZyA8LSAodGFibGVfMTBYJGF2Z19sb2dUUE1fb2xkIC0gdGFibGVfMTBYJGF2Z19sb2dUUE1feW91bmcgKQogICAgCiAgICAjIENhbGN1bGF0ZSB0aGUgbWVhbiBTdGFuZGFyZCBEZXZpYXRpb24gYmV0d2VlbiB5b3VuZyBhbmQgb2xkCiAgICB0YWJsZV8xMFgkbWVhbl9zZCA8LSBhcHBseSggWCA9ICB0YWJsZV8xMFhbLGMoInNkX2xvZ1RQTV95b3VuZyIsInNkX2xvZ1RQTV9vbGQiKV0gLCBNQVJHSU4gPSAxICwgRlVOID0gbWVhbikKICAgIAogICAgIyBGaW5hbGx5IGNhbGN1bGF0ZSBDb2hlbnMgRCwgYnkgZGV2aWRpbmcgdGhlIGRpZmZlcmVuY2Ugb2YgdGhlIG1lYW4gdmFsdWVzIGJ5IHRoZSBtZWFuIHN0YW5kYXJkIGRldmlhdGlvbiAKICAgIHRhYmxlXzEwWCQnZGlmZmVyZW5jZV9vZl9hdmcvbWVhbl9zZCcgPC0gdGFibGVfMTBYJGRpZmZlcmVuY2Vfb2ZfYXZnL3RhYmxlXzEwWCRtZWFuX3NkCgogICAgIyBBZGQgcC12YWx1ZXMgZnJvbSB0LXRlc3QKICAgIAogICAgIyBSdW4gdC10ZXN0IG9uIDEwWCBOU0MgZGF0YSBvbGQgYW5kIHlvdW5nCiAgICAKICAgICMgUHJlcGFyZSBmYWN0b3Igd2l0aCB5b3VuZyBhbmQgb2xkIGluIG9yZGVyIG9mIHRoZSBjb2x1bW4gbmFtZXMKICAgIGZhY3QgPC0gYXMuY2hhcmFjdGVyKCBjb2xuYW1lcyhUUE1fTlNDX3N1YnBvcCkgKQogICAgZmFjdFsgZ3JlcGwoeCA9IGZhY3QgLCBwYXR0ZXJuID0gIi0xIikgXSA8LSAib2xkIgogICAgZmFjdFsgZ3JlcGwoeCA9IGZhY3QgLCBwYXR0ZXJuID0gIi0yIiApIF0gPC0gInlvdW5nIgogICAgZmFjdCA8LSBmYWN0b3IoIGZhY3QgKQogICAgCiAgICAjIFByZXBhcmUgbWF0cml4IGZvciB0LXRlc3QgaW5wdXQKICAgIFRQTV9OU0NfbWF0IDwtIFRQTV9OU0Nfc3VicG9wCiAgICBUUE1fTlNDX21hdCA8LSBhcy5tYXRyaXgoIFRQTV9OU0NfbWF0ICkgCiAgICAKICAgICMgUnVuIHQtdGVzdCB3aXRoIHJvd3R0ZXN0cyBmdW5jdGlvbiBmcm9tIGdlbmVmaWx0ZXIgcGFja2FnZXMKICAgIHRfdGVzdF9OU0NzXzEwWCA8LSByb3d0dGVzdHMoeCA9IFRQTV9OU0NfbWF0ICwgZmFjID0gZmFjdCApCiAgICAKICAgICMgQWRkIGVuc2VtYmxfZ2VuZV9pZCBhcyBjb2x1bW4gZnJvbSB0aGUgcm93bmFtZXMKICAgIHRfdGVzdF9OU0NzXzEwWCRnZW5lX2lkIDwtIHJvd25hbWVzKHRfdGVzdF9OU0NzXzEwWCkgCiAgICAKICAgICMgSm9pbiB0aGUgdGFibGVzCiAgICB0YWJsZV8xMFhfbWVyZ2VkX3dpdGhfdHRlc3QgPC0gZnVsbF9qb2luKHggPSB0YWJsZV8xMFggLCB5ID0gdF90ZXN0X05TQ3NfMTBYICwgYnkgPSAiZ2VuZV9pZCIgKQoKICAgIHRhYmxlXzEwWF9tZXJnZWRfd2l0aF90dGVzdAp9CmBgYAoKUnVuIHRoZSB0LXRlc3QgZm9yIGFsbCBzdWJwb3B1bGF0aW9ucyBpbmRpdmlkdWFsbHkKCmBgYHtyfQpzdWJwb3B1bGF0aW9ucyA8LSBsaXN0KGMocU5TQzEgPSAicU5TQzEiLHFOU0MyID0gInFOU0MyIixhTlNDMCA9ICJhTlNDMCIsYU5TQzEgPSAiYU5TQzEiLGFOU0MyID0gImFOU0MyIixUQVAgPSAiVEFQIixOQiA9ICJOQiIsT1BDID0gIk9QQyIsT0QgPSAiT0QiKSkKCnR0ZXN0X3Jlc3VsdHNfY29oZW5zZCA8LSBsaXN0KAogIHFOU0MxID0gcnVuX3R0ZXN0X2FuZF9jYWxjdWxhdGVfQ29oZW5zX2QoInFOU0MxIikgLAogIHFOU0MyID0gcnVuX3R0ZXN0X2FuZF9jYWxjdWxhdGVfQ29oZW5zX2QoInFOU0MyIikgLAogIGFOU0MwID0gcnVuX3R0ZXN0X2FuZF9jYWxjdWxhdGVfQ29oZW5zX2QoImFOU0MwIikgLAogIGFOU0MxID0gcnVuX3R0ZXN0X2FuZF9jYWxjdWxhdGVfQ29oZW5zX2QoImFOU0MxIikgLAogIGFOU0MyID0gcnVuX3R0ZXN0X2FuZF9jYWxjdWxhdGVfQ29oZW5zX2QoImFOU0MyIikgLAogIFRBUCA9IHJ1bl90dGVzdF9hbmRfY2FsY3VsYXRlX0NvaGVuc19kKCJUQVAiKSAsCiAgTkIgPSBydW5fdHRlc3RfYW5kX2NhbGN1bGF0ZV9Db2hlbnNfZCgiTkIiKSAsCiAgT1BDID0gcnVuX3R0ZXN0X2FuZF9jYWxjdWxhdGVfQ29oZW5zX2QoIk9QQyIpICwKICBPRCA9IHJ1bl90dGVzdF9hbmRfY2FsY3VsYXRlX0NvaGVuc19kKCJPRCIpCikKICAKIyBPcmRlciB0aGUgdGFibGUgYnkgY29oZW5zIGQKdHRlc3RfcmVzdWx0c19jb2hlbnNkIDwtIGxhcHBseSh0dGVzdF9yZXN1bHRzX2NvaGVuc2QsIEZVTiA9IGZ1bmN0aW9uKHgpe2FycmFuZ2UoeCwgZGVzYyhkaWZmZXJlbmNlX29mX2F2Zy9tZWFuX3NkKSl9ICkKCmlmKHNhdmVfY3N2KXsKIyMgU2F2ZSB0aGUgcmVzdWx0cyBhcyBhIGNzdiBmaWxlCiAgIHdyaXRlLmNzdih4ID0gdHRlc3RfcmVzdWx0c19jb2hlbnNkW1sxXV0gLCBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL0RFX3QtdGVzdF9vbGRfdnNfeW91bmcvc3VicG9wdWxhdGlvbnMvREVfb2xkX3ZzX3lvdW5nXzEwWF90LXRlc3RfQ29oZW5zRF8iLG5hbWVzKHR0ZXN0X3Jlc3VsdHNfY29oZW5zZClbWzFdXSwiLmNzdiIpICkKICAjIAogICB3cml0ZS5jc3YoeCA9IHR0ZXN0X3Jlc3VsdHNfY29oZW5zZFtbMl1dICwgZmlsZSA9IHBhc3RlMCgicmVzdWx0cy9ERV90LXRlc3Rfb2xkX3ZzX3lvdW5nL3N1YnBvcHVsYXRpb25zL0RFX29sZF92c195b3VuZ18xMFhfdC10ZXN0X0NvaGVuc0RfIixuYW1lcyh0dGVzdF9yZXN1bHRzX2NvaGVuc2QpW1syXV0sIi5jc3YiKSApCiAgIyAKICAgd3JpdGUuY3N2KHggPSB0dGVzdF9yZXN1bHRzX2NvaGVuc2RbWzNdXSAsIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvREVfdC10ZXN0X29sZF92c195b3VuZy9zdWJwb3B1bGF0aW9ucy9ERV9vbGRfdnNfeW91bmdfMTBYX3QtdGVzdF9Db2hlbnNEXyIsbmFtZXModHRlc3RfcmVzdWx0c19jb2hlbnNkKVtbM11dLCIuY3N2IikgKQogICMgCiAgIHdyaXRlLmNzdih4ID0gdHRlc3RfcmVzdWx0c19jb2hlbnNkW1s0XV0gLCBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL0RFX3QtdGVzdF9vbGRfdnNfeW91bmcvc3VicG9wdWxhdGlvbnMvREVfb2xkX3ZzX3lvdW5nXzEwWF90LXRlc3RfQ29oZW5zRF8iLG5hbWVzKHR0ZXN0X3Jlc3VsdHNfY29oZW5zZClbWzRdXSwiLmNzdiIpICkKICAjIAogICB3cml0ZS5jc3YoeCA9IHR0ZXN0X3Jlc3VsdHNfY29oZW5zZFtbNV1dICwgZmlsZSA9IHBhc3RlMCgicmVzdWx0cy9ERV90LXRlc3Rfb2xkX3ZzX3lvdW5nL3N1YnBvcHVsYXRpb25zL0RFX29sZF92c195b3VuZ18xMFhfdC10ZXN0X0NvaGVuc0RfIixuYW1lcyh0dGVzdF9yZXN1bHRzX2NvaGVuc2QpW1s1XV0sIi5jc3YiKSApCiAgIyAKICAgd3JpdGUuY3N2KHggPSB0dGVzdF9yZXN1bHRzX2NvaGVuc2RbWzZdXSAsIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvREVfdC10ZXN0X29sZF92c195b3VuZy9zdWJwb3B1bGF0aW9ucy9ERV9vbGRfdnNfeW91bmdfMTBYX3QtdGVzdF9Db2hlbnNEXyIsbmFtZXModHRlc3RfcmVzdWx0c19jb2hlbnNkKVtbNl1dLCIuY3N2IikgKQogICMgCiAgIHdyaXRlLmNzdih4ID0gdHRlc3RfcmVzdWx0c19jb2hlbnNkW1s3XV0gLCBmaWxlID0gcGFzdGUwKCJyZXN1bHRzL0RFX3QtdGVzdF9vbGRfdnNfeW91bmcvc3VicG9wdWxhdGlvbnMvREVfb2xkX3ZzX3lvdW5nXzEwWF90LXRlc3RfQ29oZW5zRF8iLG5hbWVzKHR0ZXN0X3Jlc3VsdHNfY29oZW5zZClbWzddXSwiLmNzdiIpICkKICAjIAogICB3cml0ZS5jc3YoeCA9IHR0ZXN0X3Jlc3VsdHNfY29oZW5zZFtbOF1dICwgZmlsZSA9IHBhc3RlMCgicmVzdWx0cy9ERV90LXRlc3Rfb2xkX3ZzX3lvdW5nL3N1YnBvcHVsYXRpb25zL0RFX29sZF92c195b3VuZ18xMFhfdC10ZXN0X0NvaGVuc0RfIixuYW1lcyh0dGVzdF9yZXN1bHRzX2NvaGVuc2QpW1s4XV0sIi5jc3YiKSApCiAgIyAKICAgd3JpdGUuY3N2KHggPSB0dGVzdF9yZXN1bHRzX2NvaGVuc2RbWzldXSAsIGZpbGUgPSBwYXN0ZTAoInJlc3VsdHMvREVfdC10ZXN0X29sZF92c195b3VuZy9zdWJwb3B1bGF0aW9ucy9ERV9vbGRfdnNfeW91bmdfMTBYX3QtdGVzdF9Db2hlbnNEXyIsbmFtZXModHRlc3RfcmVzdWx0c19jb2hlbnNkKVtbOV1dLCIuY3N2IikgKQp9CmBgYAoKCiMgQ29tcGFyZSBwc2V1ZG90aW1lIHRvIFNldXJhdCBhbmFseXNpcwoKYGBge3J9CnBzZXVkb3RpbWVfb3JkZXJpbmcgPC0gcmVhZC5jc3YoIk1vbm9jbGUvTW9ub2NsZV9wc2V1ZG90aW1lX2Fzc2lnbWVudC5jc3YiICwgcm93Lm5hbWVzID0gMSkKYGBgCgpgYGB7cn0Kc2V1cmF0XzEwWDIgPC0gQWRkTWV0YURhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBtZXRhZGF0YSA9IHBzZXVkb3RpbWVfb3JkZXJpbmdbIlBzZXVkb3RpbWUiXSApCmBgYAoKIyMgdC1TTkUgcGxvdHMgd2l0aCBwc2V1ZG90aW1lCgpgYGB7cn0Kc2V1cmF0XzEwWDIgPC0gU2V0QWxsSWRlbnQoIG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWQgPSAiY2VsbHR5cGUiICkKClRTTkVQbG90KHNldXJhdF8xMFgyKQpgYGAKCiMjIE92ZXJsYXkgcHNldWRvdGltZSBhcyBjb2xvciBvbnRvIHRoZSB0LVNORSBwbG90CgpXZSBmZXRjaCB0aGUgdC1TTkUgY29vcmRpbmF0ZXMgYW5kIHRoZSBQc2V1ZG90aW1lIGFzc2lnbm1lbnQgdmFsdWVzIGFuZCBwbG90IHRoZSB0LVNORSBwbG90IHdpdGggcHNldWRvdGltZSBvdmVybGF5ZWQuCgpgYGB7cn0KZGZfcGxvdCA8LSBGZXRjaERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIsIHZhcnMuYWxsID0gYygidFNORV8xIiwidFNORV8yIiwiYWdlIiwiUHNldWRvdGltZSIsImNlbGx0eXBlIikgKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbGJhcmNvZGUiKSAlPiUKICAgICAgICAgICAgZmlsdGVyKCEgaXMubmEoUHNldWRvdGltZSkpCgpnLmVxIDwtIGdncGxvdCgKICAgICAgZGF0YSA9IGRmX3Bsb3QsCiAgICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgICAgeCA9IHRTTkVfMSwKICAgICAgICB5ID0gdFNORV8yLAogICAgICAgIGNvbG9yID0gUHNldWRvdGltZQogICAgICAgICkKICAgICApICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXMob3B0aW9uID0gIkQiKSArCiAgY29vcmRfZXF1YWwoKQoKZy5lcQpgYGAKCgoKIyBNb25vY2xlIHRyYWplY3RvcnkgcGxvdCB3aXRoIGNvbG9yZWQgY2VsbCBpZGVudGl0aWVzCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTN9CnBzZXVkb3RpbWVfb3JkZXJpbmdfaWRlbnQgPC0gZGF0YS5mcmFtZSggQ2VsbF9UeXBlX1NldXJhdCA9IHNldXJhdF8xMFgyQGlkZW50KSAlPiUgCiAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbGJhcmNvZGUiKSAlPiUgCiAgICAgICAgICAgICAgICAgIGZ1bGxfam9pbiggcHNldWRvdGltZV9vcmRlcmluZyAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbGJhcmNvZGUiKSAKICAgICAgICAgICAgICAgICAgICAsIGJ5ID0gImNlbGxiYXJjb2RlIiApICU+JSAKICAgICAgICAgICAgICAgICAgZmlsdGVyKCEgQ2VsbF9UeXBlX1NldXJhdCAlaW4lIGMoIk9QQyIsIk9EIixOQSkgKSAlPiUgCiAgICAgICAgICAgICAgICAgIGZpbHRlcighIGlzLm5hKFBzZXVkb3RpbWUpKQpgYGAKCmBgYHtyICwgZmlnLndpZHRoPTE1fQpwMl9pZGVudC5zcGxpdC5oX3BjaDIxIDwtIGdncGxvdChwc2V1ZG90aW1lX29yZGVyaW5nX2lkZW50LCBhZXMoeCA9IERpbTEsIHkgPSBEaW0yLCBmaWxsID0gZmFjdG9yKCBDZWxsX1R5cGVfU2V1cmF0ICwgbGV2ZWxzID0gYygicU5TQzEiLCJxTlNDMiIsImFOU0MwIiwiYU5TQzEiLCJhTlNDMiIsIlRBUCIsIk5CIikpICksIGFscGhhID0gMSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAzICwgYWxwaGEgPSAxICwgY29sb3IgPSAiYmxhY2siICwgcGNoID0gMjEgKSArCiAgIyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsgCiAgZ2d0aXRsZSgnVHJhamVjdG9yeSBvZiBOU0MgTGluZWFnZSBhcyBhcnJhbmdlZCBieSBNb25vY2xlJykgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCBxTlNDMSA9ICJzdGVlbGJsdWUiICwgcU5TQzIgPSAic3RlZWxibHVlMSIgLCBhTlNDMCA9ICJ0b21hdG8iICwgYU5TQzEgPSAic2llbm5hMSIsIGFOU0MyID0gInNpZW5uYTMiICwgVEFQID0gImdyZWVuIiAsIE5CID0gInllbGxvdyIgKSAsIG5hbWUgPSAiQWN0aXZhdGlvbiBzdGF0ZSIpICsKICAjIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkgKSArCiAgIyB0aGVtZV9jbGFzc2ljKCkgKwogIGNvb3JkX2VxdWFsKCkgKwogIGZhY2V0X2dyaWQoLn5BZ2UpIAoKcDJfaWRlbnQuc3BsaXQuaF9wY2gyMQpgYGAKClBsb3QgZGVuc2l0eSBhbG9uZyBwc2V1ZG90aW1lIGZvciBvbGQgYW5kIHlvdW5nCgpgYGB7cn0KZyA8LSBnZ3Bsb3QoZGF0YSA9IHBzZXVkb3RpbWVfb3JkZXJpbmdfaWRlbnQgLCBhZXMoeCA9IFBzZXVkb3RpbWUgLCBjb2xvciA9IENlbGxfVHlwZV9TZXVyYXQgLCBmaWxsID0gQ2VsbF9UeXBlX1NldXJhdCApKSArIGdlb21fZGVuc2l0eShidz0yLCBhbHBoYSA9IDAuNykgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSAgIGMoIHFOU0MxID0gInN0ZWVsYmx1ZSIgLCBxTlNDMiA9ICJzdGVlbGJsdWUxIiAsIGFOU0MwID0gInRvbWF0byIgLCBhTlNDMSA9ICJzaWVubmExIiwgYU5TQzIgPSAic2llbm5hMyIgLCBUQVAgPSAiZ3JlZW4iICwgTkIgPSAieWVsbG93IikgKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSAgIGMoIHFOU0MxID0gInN0ZWVsYmx1ZSIgLCBxTlNDMiA9ICJzdGVlbGJsdWUxIiAsIGFOU0MwID0gInRvbWF0byIgLCBhTlNDMSA9ICJzaWVubmExIiwgYU5TQzIgPSAic2llbm5hMyIgLCBUQVAgPSAiZ3JlZW4iICwgTkIgPSAieWVsbG93IikgKSAKCmcKYGBgCgpgYGB7cn0KZyA8LSBnZ3Bsb3QoZGF0YSA9IHBzZXVkb3RpbWVfb3JkZXJpbmdfaWRlbnQgLCBhZXMoeCA9IFBzZXVkb3RpbWUgLCBjb2xvciA9IEFnZSAsIGZpbGwgPSBBZ2UpICkgKyBnZW9tX2RlbnNpdHkoYnc9MiwgYWxwaGEgPSAwLjEpCgpnCmBgYAoKR08gQW5hbHlzaXMgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGluIGNlbGx0eXBlcwoKRGVmaW5lIGZ1bmN0aW9uIHRvIHJ1biBHTyBhbmQgR1NFQSBhbmFseXNpcyBhbmQgbWFrZSBhIGRvdHBsb3QgZnJvbSB0aGUgcmVzdWx0cwoKR08gdGVybSBhbmFseXNpcwoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTIwfQpHT19hbmFseXNpcyA8LSBmdW5jdGlvbihERV9yZXN1bHRzX1NldXJhdF9saXN0ICwgcF9hZGpfY3V0b2ZmID0gMC4wNSAsIGF2Z19sb2dGQ19jdXRvZmYgPSAwLjMgKXsgICMgbG9nMTAoMikgPSAwLjMKICByZXF1aXJlKCJjbHVzdGVyUHJvZmlsZXIiKQogIAogIGlmKCBsZW5ndGgoREVfcmVzdWx0c19TZXVyYXRfbGlzdCkgPCAxKXsKICAgIHdhcm5pbmcoIk5vIGVudHJpZXMgaW4gREVfcmVzdWx0c19TZXVyYXRfbGlzdCIpCiAgICBpbnZpc2libGUoREVfcmVzdWx0c19TZXVyYXRfbGlzdCkKICB9CiAgCiAgZGVfZ2VuZXNfbGlzdCA8LWxpc3QoTlVMTCkKICBmb3IoaSBpbiBzZXFfbGVuKGxlbmd0aChERV9yZXN1bHRzX1NldXJhdF9saXN0KSkgKXsKICAgIGRlX2dlbmVzIDwtIERFX3Jlc3VsdHNfU2V1cmF0X2xpc3RbW2ldXSAlPiUgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImdlbmUiKSAlPiUgZHBseXI6OmZpbHRlciggcF92YWxfYWRqIDwgcF9hZGpfY3V0b2ZmICYgYWJzKGF2Z19sb2dGQykgPiBhdmdfbG9nRkNfY3V0b2ZmICkgJT4lIGRwbHlyOjpwdWxsKGdlbmUpCiAgICAKICAgIGRlX2dlbmVzX2xpc3RbWyBuYW1lcyhERV9yZXN1bHRzX1NldXJhdF9saXN0W2ldKSBdXSA8LSBiaXRyKGdlbmVJRCA9IGRlX2dlbmVzICwgZnJvbVR5cGUgPSAiU1lNQk9MIiAsIHRvVHlwZSA9ICJFTlRSRVpJRCIgLCBPcmdEYiA9ICJvcmcuTW0uZWcuZGIiIClbLCJFTlRSRVpJRCJdCiAgfQoKICAgICMgYXZhaWxhYmxlIG9udG9sb2dpZXMgYXJlOgogICAgIyBCUCAtIGJpb2xvZ2ljYWxfcHJvY2VzcwogICAgIyBDQyAtIGNlbGx1bGFyX2NvbXBvbmVudAogICAgIyBNRiAtIG1vbGVjdWxhcl9mdW5jdGlvbgogICAgZ29fcmVzdWx0cyA8LSBjb21wYXJlQ2x1c3RlcihnZW5lQ2x1c3RlcnMgPSBkZV9nZW5lc19saXN0ICwgZnVuID0gImVucmljaEdPIiAsIE9yZ0RiID0gIm9yZy5NbS5lZy5kYiIgLCBvbnQgPSAiQlAiLCBwdmFsdWVDdXRvZmYgPSAwLjA1ICApCiAgCiAgICBjbHVzdGVyUHJvZmlsZXI6OnBsb3QoZ29fcmVzdWx0cyApCiAgICAgIAogIHJldHVybihnb19yZXN1bHRzKQp9CmBgYAoKTG9hZCB0aGUgcmVzdWx0cyBvZiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gdGVzdHMgYW5kIGNoZWNrIHRoZSBhc3NvY2lhdGVkIEdPIHRlcm1zIGJ5IGVucmljaG1lbnQgYW5hbHlzaXMKCiMjIEdvIFRlcm0gYW5hbHlzaXMgZm9yIERpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBiZXR3ZWVuIHRoZSBpbmRpdmlkdWFsIGNlbGx0eXBlcyAoZmluZGluZyBtYXJrZXIgZ2VuZXMgZm9yIHRoZSBjZWxsdHlwZXMpCgpgYGB7cn0KCkRFX2NlbGx0eXBlX2dlbmVzX3FOU0MxIDwtIHJlYWQuY3N2KGZpbGUgPSAiY2VsbHR5cGVfbWFya2Vycy9jZWxsdHlwZV9xTlNDMV91cHJlZ3VsYXRlZF9tYXJrZXJzXzEwWDJfeW91bmdfb2xkLmNzdiIgICwgaGVhZGVyID0gVFJVRSAsIHJvdy5uYW1lcyA9IDEgKQpERV9jZWxsdHlwZV9nZW5lc19xTlNDMiA8LSByZWFkLmNzdihmaWxlID0gImNlbGx0eXBlX21hcmtlcnMvY2VsbHR5cGVfcU5TQzJfdXByZWd1bGF0ZWRfbWFya2Vyc18xMFgyX3lvdW5nX29sZC5jc3YiICwgaGVhZGVyID0gVFJVRSAsIHJvdy5uYW1lcyA9IDEgKQpERV9jZWxsdHlwZV9nZW5lc19hTlNDMCA8LSByZWFkLmNzdihmaWxlID0gImNlbGx0eXBlX21hcmtlcnMvY2VsbHR5cGVfYU5TQzBfdXByZWd1bGF0ZWRfbWFya2Vyc18xMFgyX3lvdW5nX29sZC5jc3YiICwgaGVhZGVyID0gVFJVRSAsIHJvdy5uYW1lcyA9IDEgKQpERV9jZWxsdHlwZV9nZW5lc19hTlNDMSA8LSByZWFkLmNzdihmaWxlID0gImNlbGx0eXBlX21hcmtlcnMvY2VsbHR5cGVfYU5TQzFfdXByZWd1bGF0ZWRfbWFya2Vyc18xMFgyX3lvdW5nX29sZC5jc3YiICwgaGVhZGVyID0gVFJVRSAsIHJvdy5uYW1lcyA9IDEgKQpERV9jZWxsdHlwZV9nZW5lc19hTlNDMiA8LSByZWFkLmNzdihmaWxlID0gImNlbGx0eXBlX21hcmtlcnMvY2VsbHR5cGVfYU5TQzJfdXByZWd1bGF0ZWRfbWFya2Vyc18xMFgyX3lvdW5nX29sZC5jc3YiICwgaGVhZGVyID0gVFJVRSAsIHJvdy5uYW1lcyA9IDEgKQpERV9jZWxsdHlwZV9nZW5lc19UQVAgPC0gcmVhZC5jc3YoZmlsZSA9ICJjZWxsdHlwZV9tYXJrZXJzL2NlbGx0eXBlX1RBUF91cHJlZ3VsYXRlZF9tYXJrZXJzXzEwWDJfeW91bmdfb2xkLmNzdiIgLCBoZWFkZXIgPSBUUlVFICwgcm93Lm5hbWVzID0gMSApCkRFX2NlbGx0eXBlX2dlbmVzX05CIDwtIHJlYWQuY3N2KGZpbGUgPSAiY2VsbHR5cGVfbWFya2Vycy9jZWxsdHlwZV9OQl91cHJlZ3VsYXRlZF9tYXJrZXJzXzEwWDJfeW91bmdfb2xkLmNzdiIgLCBoZWFkZXIgPSBUUlVFICwgcm93Lm5hbWVzID0gMSApCkRFX2NlbGx0eXBlX2dlbmVzX09QQyA8LSByZWFkLmNzdihmaWxlID0gImNlbGx0eXBlX21hcmtlcnMvY2VsbHR5cGVfT1BDX3VwcmVndWxhdGVkX21hcmtlcnNfMTBYMl95b3VuZ19vbGQuY3N2IiAsIGhlYWRlciA9IFRSVUUgLCByb3cubmFtZXMgPSAxICkKREVfY2VsbHR5cGVfZ2VuZXNfT0QgPC0gcmVhZC5jc3YoZmlsZSA9ICJjZWxsdHlwZV9tYXJrZXJzL2NlbGx0eXBlX09EX3VwcmVndWxhdGVkX21hcmtlcnNfMTBYMl95b3VuZ19vbGQuY3N2IiAsIGhlYWRlciA9IFRSVUUgLCByb3cubmFtZXMgPSAxICkKYGBgCgpgYGB7cn0KY2VsbHR5cGVfb3JkZXIgPC0gYygicU5TQzEiLCJxTlNDMiIsImFOU0MwIiwiYU5TQzEiLCJhTlNDMiIsIlRBUCIsIk5CIiwiT1BDIiwiT0QiKQpgYGAKCgpGaXJzdCB3ZSBsb2FkIGFsbCBvYmplY3RzIHN0YXJ0aW5nIHdpdGggREVfZ2VuZXMgaW50byBvbmUgbGlzdCB3aXRoIHRoZSBvYmplY3QgbmFtZXMgYmVjb21pbmcgdGhlIGxpc3QgbmFtZSBpZGVudGlmaWVycyBhbmQgd2UgZmlsdGVyIHRoZSBjb250YWluZWQgZGF0YSBmcmFtZXMgZm9yIGEgdmFsdWUgMCBvciBiaWdnZXIgaW4gdGhlIGF2Z19sb2dGQyBjb2x1bW4KCmBgYHtyfQpkZl9saXN0X0RFX2dlbmVzX2NlbGx0eXBlcyA8LSBtZ2V0KCBwYXN0ZSggIkRFX2NlbGx0eXBlX2dlbmVzIiAsIGNlbGx0eXBlX29yZGVyICwgc2VwID0gIl8iKSApCmRmX2xpc3RfREVfZ2VuZXNfY2VsbHR5cGVzIDwtIGxhcHBseShkZl9saXN0X0RFX2dlbmVzX2NlbGx0eXBlcywgRlVOID0gZnVuY3Rpb24oeCkgZmlsdGVyKHgsYXZnX2xvZ0ZDPj0wKSApIApgYGAKCk5vdyB3ZSBjYW4gY2hhbmdlIHRoZSBuYW1lIG9mIHRoZSBsaXN0IGZpZWxkcyB0byBqdXN0IHRoZSBjZWxsdHlwZXMgLi4uIAoKYGBge3J9Cm5hbWVzKGRmX2xpc3RfREVfZ2VuZXNfY2VsbHR5cGVzKSA8LSBzdHJfcmVwbGFjZShzdHJpbmcgPSBuYW1lcyhkZl9saXN0X0RFX2dlbmVzX2NlbGx0eXBlcykgLCBwYXR0ZXJuID0gIl5ERV9nZW5lc18iICxyZXBsYWNlbWVudCA9ICIiICkKCmRmX2xpc3RfREVfZ2VuZXNfY2VsbHR5cGVzIDwtIGxhcHBseShkZl9saXN0X0RFX2dlbmVzX2NlbGx0eXBlcyAsIEZVTiA9IGZ1bmN0aW9uKHgpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKHgpIDwtIE5VTEwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdG9fcm93bmFtZXMoZGYgPSB4LCB2YXIgPSAiZ2VuZSIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pCmBgYAoKYW5kIHJ1biBHT19hbmFseXNpcyBmdW5jdGlvbiBvbiBpdAoKYGBge3J9CmdvX2NlbGx0eXBlcyA8LSBHT19hbmFseXNpcyhERV9yZXN1bHRzX1NldXJhdF9saXN0ID0gZGZfbGlzdF9ERV9nZW5lc19jZWxsdHlwZXMgKQpgYGAKCmBgYHtyfQpnb19yZXMgPC0gZ29fY2VsbHR5cGVzQGNvbXBhcmVDbHVzdGVyUmVzdWx0CgojIEN1dCBuYW1lcyBpbiBjb2x1bW4gQ2x1c3RlciB0byBqdXN0IHRoZSBhY3RpdmF0aW9uIHN0YXRlOiBlLmcuICJERV9jZWxsdHlwZV9nZW5lc19xTlNDMSIgdG8gInFOU0MxIgpnb19yZXMkQ2x1c3RlciA8LSBzdWIoIGdvX3JlcyRDbHVzdGVyICwgcGF0dGVybiA9ICIuK18iICwgcmVwbGFjZW1lbnQgPSAiIiApCgojIHdyaXRlLmNzdih4ID0gZ29fcmVzICwgZmlsZSA9ICJHT19yZXN1bHRzX2NvbXBhcmVfY2VsbHR5cGVzXzEwWC5jc3YiKQpgYGAKCmFuZCBhZnRlciBzaW1wbGlmeWluZyB0aGUgR08gdGVybXMgZW5yaWNoZWQgaW4gdGhlIGdlbmVzCgpgYGB7cn0KZ29fY2VsbHR5cGVzLnNpbSA8LSBzaW1wbGlmeShnb19jZWxsdHlwZXMpCmBgYAoKQ3VzdG9tIHBsb3Qgb2YgR08gY2F0ZWdvcmllcyBmb3IgYWN0aXZhdGlvbiBzdGF0ZXMgYWxvbmcgbGluZWFnZQoKYGBge3IgZmlnLndpZHRoPTEzICwgZmlnLmhlaWdodD05fQpnZzIgPC0gY2x1c3RlclByb2ZpbGVyOjpwbG90KGdvX2NlbGx0eXBlcy5zaW0pCgpwbG90ZGF0YSA8LSBnZzIkZGF0YSAlPiUgZmlsdGVyKCBwLmFkanVzdCA8IDAuMDUpCgpsZXZlbHMocGxvdGRhdGEkQ2x1c3RlcikgPC0gc3ViKCB4ID0gbGV2ZWxzKHBsb3RkYXRhJENsdXN0ZXIpICwgcGF0dGVybiA9ICJERV9jZWxsdHlwZV9nZW5lc18iLCByZXBsYWNlbWVudCA9ICIiKQoKZ2dfY2x1c3RlckNvbXBhcmUgPC0gZ2dwbG90KAogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHBsb3RkYXRhICwgCiAgICAgICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKCAKICAgICAgICAgICAgICAgICAgICAgICAgeCA9IENsdXN0ZXIgLCAKICAgICAgICAgICAgICAgICAgICAgICAgeSA9IERlc2NyaXB0aW9uICwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBHZW5lUmF0aW8gLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAtbG9nMTAocC5hZGp1c3QpIAogICAgICAgICAgICAgICAgICAgICAgICApIAogICAgICAgICAgICAgICAgICAgICAgKSArIAogICAgICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KCkgKyAKICAgICAgICAgICAgICAgICAgICAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gImJsdWUiICwgaGlnaCA9ICJyZWQiICAsIGJyZWFrcyA9IGMoMSwxMCwyMCwzMCw0MCkgLCBsaW1pdHMgPSBjKDEsNTApICkgKyAKICAgICAgICAgICAgICAgICAgICAgdGhlbWVfYncoKSArIAogICAgICAgICAgICAgICAgICAgICB0aGVtZSggCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3VyPSJibGFjayIsc2l6ZT0xMSksIAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG91cj0iYmxhY2siLHNpemU9MTEpIAogICAgICAgICAgICAgICAgICAgICApCgpnZ19jbHVzdGVyQ29tcGFyZQpgYGAKCiMgSGVhdG1hcHMKCmBgYHtyfQpuZXVyb25hbF9saW5lYWdlIDwtIFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC5yZW1vdmUgPSBjKCJPRCIsIk9QQyIpKQpgYGAKCgojIyBIZWF0bWFwIHdpdGggZ2VuZXMgZnJvbSBCYXNhayBldCBhbC4gLCBidXQgb3JkZXJlZCBhY2NvcmRpbmcgdG8gY2VudGVyIG9mIG1hc3MgYWxvbmcgcHNldWRvdGltZQoKYGBge3J9Cm1hcmtlcnMgPC0gYygiQWd0IiwgIlNsYzZhOSIsICJFdG5wcGwiLCAiU2xjNmExIiwgIlNwYXJjIiwgIlNsYzFhMyIsICJCY2FuIiwgCiJUc3BhbjciLCAiSHRyYTEiLCAiQ2xkbjEwIiwgIlB0biIsICJBY3NsNiIsICJGZ2ZyMyIsICJTcGFyY2wxIiwgCiJBdHAxYTIiLCAiR3ByMzdsMSIsICJHamExIiwgIlBybnAiLCAiQWNzbDMiLCAiQXFwNCIsICJBcG9lIiwgCiJHbTI2OTE3IiwgIkNzdDMiLCAiQ2x1IiwgIlNsYzFhMiIsICJQcmR4NiIsICJNdDEiLCAiQWxkb2MiLCAKIlRoYnM0IiwgIk50cmsyIiwgIkZ4eWQxIiwgIkdzdG0xIiwgIklnZmJwNSIsICJTMTAwYTYiLCAiSXRtMmIiLCAKIlNmcnAxIiwgIkRrazMiLCAiQzRiIiwgIkFjb3QxIiwgIkx1YzdsMyIsICJDa2IiLCAiQ3BlIiwgIkRiaSIsIAoiTWlhdCIsICJMaW1hMSIsICJQYWJwYzEiLCAiQXNjbDEiLCAiUnBsMTIiLCAiTXljbiIsICJPbGlnMiIsIAoiUGNuYSIsICJIc3A5MGFhMSIsICJIbnJucGFiIiwgIlJhbiIsICJQcGlhIiwgIkVlZjFhMSIsICJQdG1hIiwgCiJScGw0MSIsICJOcG0xIiwgIlJwc2EiLCAiRmFicDciLCAiRWdmciIsICJNa2k2NyIsICJEbHgyIiwgIkRseDEiLCAKIkNkY2EzIiwgIkRseDFhcyIsICJOcmVwIiwgIlR1YmIyYiIsICJEY3giLCAiQnRnMSIsICJOZmliIiwgIkdhZDEiLCAKIk5kcmc0IiwgIlNuYXAyNSIsICJTeXQxIiwgIlJiZm94MyIsICJUbXNiMTAiLCAiU3RtbjIiLCAiQ2QyNGEiLCAKIkRseDZvczEiLCAiVHViYjUiLCAiVHViYjMiLCAiQ2NuZDIiLCAiSG1nbjIiLCAiSDJhZnoiLCAiU294MTEiLCAKIlR1YmExYiIsICJUbXNiNHgiLCAiU3RtbjEiLCAiVHB0MSIsICJScGwxOGEiKQpgYGAKCkZldGNoIHRoZSBkYXRhCgpgYGB7cn0KZXhwciA8LSBGZXRjaERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCB2YXJzLmFsbCA9IG1hcmtlcnMgLCBjZWxscy51c2UgPSBuZXVyb25hbF9saW5lYWdlICkKCnBsb3REZl9leHByIDwtICBGZXRjaERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCB2YXJzLmFsbCA9IGMoIlBzZXVkb3RpbWUiKSAsIGNlbGxzLnVzZSA9IG5ldXJvbmFsX2xpbmVhZ2UgKSAlPiUKICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbGJhcmNvZGUiKSAlPiUgCiAgICAgICAgICAgICAgICBmdWxsX2pvaW4oIGFzLmRhdGEuZnJhbWUoZXhwcikgJT4lIAogICAgICAgICAgICAgICAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gImNlbGxiYXJjb2RlIikgCiAgICAgICAgICAgICAgICAsIGJ5ID0gImNlbGxiYXJjb2RlIiApICU+JSAKICAgICAgICAgICAgICAgIGZpbHRlcighIGlzLm5hKFBzZXVkb3RpbWUpKSAlPiUKICAgICAgICAgICAgICAgIGFycmFuZ2UoUHNldWRvdGltZSkKYGBgCgpgYGB7cn0KcGxvdERmX2V4cHIgCmBgYAoKYGBge3J9CmV4cHJfbWF0IDwtIHBsb3REZl9leHByICU+JSBkcGx5cjo6c2VsZWN0KC1jZWxsYmFyY29kZSwtUHNldWRvdGltZSkgJT4lIGFzLm1hdHJpeCgpCmBgYAoKCmBgYHtyfQpjZW50ZXJfb2ZfbWFzcyA8LSBhcHBseShYID0gZXhwcl9tYXQgLE1BUkdJTiA9IDIgLCBGVU4gPSBmdW5jdGlvbih4KXsgbWluKHdoaWNoKHN1bSh4KS8yIDw9IGN1bXN1bSh4KSkpIH0gKQpgYGAKCmBgYHtyfQptYXJrZXJzX2NlbnRlcl9vZl9tYXNzIDwtIG5hbWVzKHNvcnQoY2VudGVyX29mX21hc3MpKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMH0KRG9IZWF0bWFwKG9iamVjdCA9IHNldXJhdF8xMFgyICwgZ2VuZXMudXNlID0gbWFya2Vyc19jZW50ZXJfb2ZfbWFzcyAsIHNsaW0uY29sLmxhYmVsID0gVFJVRSAsIGNvbC5sb3cgPSAiYmx1ZSIgLCBjb2wubWlkID0gIndoaXRlIiAsIGNvbC5oaWdoID0gInJlZCIgLCBncm91cC5sYWJlbC5yb3QgPSBUUlVFLCBncm91cC5vcmRlciA9IGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMCIsImFOU0MxIiwiYU5TQzIiLCJUQVAiLCJOQiIsIk9QQyIsIk9EIikgLCB1c2Uuc2NhbGVkID0gVFJVRSAsIGNleC5yb3cgPSAzICwgZ3JvdXAuY2V4ID0gMTAgKSAKYGBgCgojIyBIZWF0bWFwIHdpdGggbWFya2VyIGdlbmVzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiB0aGUgY2VsbHR5cGVzCgpSZW5hbWUgdGhlIG9iamVjdHMgYW5kIGNvbnZlcnQgY29sdW1uIGdlbmUgdG8gcm93bmFtZXMgaW4gZXZlcnkgZGF0YS5mcmFtZSBpbiB0aGUgbGlzdAoKYGBge3J9CmRmX2xpc3RfREVfZ2VuZXNfY2VsbHR5cGVzIDwtIG1nZXQoIHBhc3RlKCAiREVfY2VsbHR5cGVfZ2VuZXMiICwgYygicU5TQzEiLCJxTlNDMiIsImFOU0MwIiwiYU5TQzEiLCJhTlNDMiIsIlRBUCIsIk5CIikgLCBzZXAgPSAiXyIpICkKZGZfbGlzdF9ERV9nZW5lc19jZWxsdHlwZXMgPC0gbGFwcGx5KGRmX2xpc3RfREVfZ2VuZXNfY2VsbHR5cGVzLCBGVU4gPSBmdW5jdGlvbih4KSBmaWx0ZXIoeCxhdmdfbG9nRkM+PTApICkgCgpuYW1lcyhkZl9saXN0X0RFX2dlbmVzX2NlbGx0eXBlcykgPC0gc3RyX3JlcGxhY2Uoc3RyaW5nID0gbmFtZXMoZGZfbGlzdF9ERV9nZW5lc19jZWxsdHlwZXMpICwgcGF0dGVybiA9ICJeREVfZ2VuZXNfIiAscmVwbGFjZW1lbnQgPSAiIiApCmBgYAoKYGBge3J9CiMgR2V0IGFsbCBuYW1lcyBmcm9tIHRoZSBzaWcuIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBjZWxsdHlwZSBtYXJrZXJzIGdlbmVzCm1hcmtlcnNfY2VudGVyX29mX21hc3NfZGUgPC0gbGFwcGx5KGRmX2xpc3RfREVfZ2VuZXNfY2VsbHR5cGVzICwgRlVOID0gZnVuY3Rpb24oeCl7IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoeCwgIHBfdmFsX2FkaiA8IDAuMDUgKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChnZW5lKSB9ICkKYGBgCgpgYGB7cn0KbWFya2Vyc19jZW50ZXJfb2ZfbWFzc19kZSA8LSB1bmlxdWUodW5saXN0KG1hcmtlcnNfY2VudGVyX29mX21hc3NfZGUpKQpgYGAKCmBgYHtyfQpleHByX2RlIDwtIEZldGNoRGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIHZhcnMuYWxsID0gbWFya2Vyc19jZW50ZXJfb2ZfbWFzc19kZSAsIGNlbGxzLnVzZSA9IG5ldXJvbmFsX2xpbmVhZ2UgKQoKcGxvdERmX2V4cHJfZGUgPC0gIEZldGNoRGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIHZhcnMuYWxsID0gYygiUHNldWRvdGltZSIpICwgY2VsbHMudXNlID0gbmV1cm9uYWxfbGluZWFnZSApICU+JQogICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJjZWxsYmFyY29kZSIpICU+JSAKICAgICAgICAgICAgICAgIGZ1bGxfam9pbiggYXMuZGF0YS5mcmFtZShleHByX2RlKSAlPiUgCiAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiY2VsbGJhcmNvZGUiKSAKICAgICAgICAgICAgICAgICwgYnkgPSAiY2VsbGJhcmNvZGUiICkgJT4lIAogICAgICAgICAgICAgICAgZmlsdGVyKCEgaXMubmEoUHNldWRvdGltZSkpICU+JQogICAgICAgICAgICAgICAgYXJyYW5nZShQc2V1ZG90aW1lKQpgYGAKCkNvbnZlcnQgdG8gbWF0cml4CgpgYGB7cn0KZXhwcl9tYXRfZGUgPC0gcGxvdERmX2V4cHJfZGUgJT4lIGRwbHlyOjpzZWxlY3QoLWNlbGxiYXJjb2RlLC1Qc2V1ZG90aW1lKSAlPiUgYXMubWF0cml4KCkKYGBgCgpDYWxjdWxhdGUgdGhlIGNlbnRlciBvZiBtYXNzIGZvciBldmVyeSBnZW5lCgpgYGB7cn0KY2VudGVyX29mX21hc3NfZGUgPC0gYXBwbHkoWCA9IGV4cHJfbWF0X2RlICxNQVJHSU4gPSAyICwgRlVOID0gZnVuY3Rpb24oeCl7IG1pbih3aGljaChzdW0oeCkvMiA8PSBjdW1zdW0oeCkpKSB9ICkKYGBgCgpgYGB7cn0KbGVuZ3RoKGNlbnRlcl9vZl9tYXNzX2RlKQpgYGAKCmBgYHtyfQptYXJrZXJzX2NlbnRlcl9vZl9tYXNzX2RlIDwtIG5hbWVzKHNvcnQoY2VudGVyX29mX21hc3NfZGUpKQoKbWFya2Vyc19jZW50ZXJfb2ZfbWFzc19kZV93dHMgPC0gc29ydChjZW50ZXJfb2ZfbWFzc19kZSkKYGBgCgpOb3cgd2UgYWxzbyB0YWtlIHRoZSBtYXJrZXJzIGZvciB0aGUgT2xpZ29kZW5kcm9jeXRlcyBhbmQgdGhlaXIgUHJvZ2VuaXRvcnMgYW5kIGFwcGVuZCB0aGVtCgpgYGB7cn0KZ2VuZXNfT0QgPC0gYXMuY2hhcmFjdGVyKCBERV9jZWxsdHlwZV9nZW5lc19PRCAlPiUgZHBseXI6OmZpbHRlciggcF92YWxfYWRqIDwgMC4wNSApICU+JSBwdWxsKGdlbmUpICkKCmdlbmVzX09EX3d0cyA8LSByZXAoMSwgbGVuZ3RoKGdlbmVzX09EKSkKbmFtZXMoZ2VuZXNfT0Rfd3RzKSA8LSAgZ2VuZXNfT0QKYGBgCgpgYGB7cn0KZ2VuZXNfT1BDIDwtIGFzLmNoYXJhY3RlciggREVfY2VsbHR5cGVfZ2VuZXNfT1BDICU+JSBkcGx5cjo6ZmlsdGVyKCBwX3ZhbF9hZGogPCAwLjA1ICkgJT4lIHB1bGwoZ2VuZSkgKQoKZ2VuZXNfT1BDX3d0cyA8LSByZXAoMCwgbGVuZ3RoKGdlbmVzX09QQykpCm5hbWVzKGdlbmVzX09QQ193dHMpIDwtICBnZW5lc19PUEMKYGBgCgpBcHBlbmQgdGhlIGdlbmVzIHRvIHRoZSBsaXN0IG9mIG1hcmtlcnMKCmBgYHtyfQptYXJrZXJzX2NlbnRlcl9vZl9tYXNzX2RlIDwtIGMobWFya2Vyc19jZW50ZXJfb2ZfbWFzc19kZSwgZ2VuZXNfT1BDICwgZ2VuZXNfT0QgKQptYXJrZXJzX2NlbnRlcl9vZl9tYXNzX2RlIDwtIHVuaXF1ZShtYXJrZXJzX2NlbnRlcl9vZl9tYXNzX2RlKQoKbWFya2Vyc19jZW50ZXJfb2ZfbWFzc19kZV93dHMgPC0gYyhtYXJrZXJzX2NlbnRlcl9vZl9tYXNzX2RlX3d0cyAsIGdlbmVzX09QQ193dHMgLCBnZW5lc19PRF93dHMpCm1hcmtlcnNfY2VudGVyX29mX21hc3NfZGVfd3RzIDwtIG1hcmtlcnNfY2VudGVyX29mX21hc3NfZGVfd3RzWyB1bmlxdWUobmFtZXMobWFya2Vyc19jZW50ZXJfb2ZfbWFzc19kZV93dHMpKSBdCmBgYAoKUGxvdCBIZWF0bWFwCgpgYGB7ciBmaWcud2lkdGg9MTB9CkRvSGVhdG1hcChvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGdlbmVzLnVzZSA9IG1hcmtlcnNfY2VudGVyX29mX21hc3NfZGUgLCBzbGltLmNvbC5sYWJlbCA9IFRSVUUgLCBncm91cC5jZXggPSAxMCAsICBjb2wubG93ID0gImJsdWUiICwgY29sLm1pZCA9ICJ3aGl0ZSIgLCBjb2wuaGlnaCA9ICJyZWQiICwgZ3JvdXAubGFiZWwucm90ID0gVFJVRSwgZ3JvdXAub3JkZXIgPSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzAiLCJhTlNDMSIsImFOU0MyIiwiVEFQIiwiTkIiLCJPUEMiLCJPRCIpICwgdXNlLnNjYWxlZCA9IFRSVUUgLCBjZXgucm93ID0gMCApIApgYGAKCiMgUGxvdCBhdmVyYWdlIGV4cHJlc3Npb24gcGVyIGdlbmUgeW91bmcgdnMgb2xkCgojIyBTcGxpdCB0aGUgMTBYIGRhdGEgYnkgYWdlCgpgYGB7cn0Kc2V1cmF0XzEwWDJfeW91bmcgPC0gU3Vic2V0RGF0YSggb2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBzdWJzZXQubmFtZSA9ICJhZ2VfbnVtIiAsICBhY2NlcHQubG93ID0gMS41ICkKc2V1cmF0XzEwWDJfb2xkIDwtIFN1YnNldERhdGEoIG9iamVjdCA9IHNldXJhdF8xMFgyICwgc3Vic2V0Lm5hbWUgPSAiYWdlX251bSIgLCAgYWNjZXB0LmhpZ2ggPSAxLjUgKQpgYGAKCiMjIEFsbCBjZWxscwoKYGBge3IgZmlnLmhlaWdodD01ICwgZmlnLndpZHRoPTV9CmxpYnJhcnkoZ2dyZXBlbCkKCm9sZF9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24ob2JqZWN0ID0gU2V0SWRlbnQoIG9iamVjdCA9IHNldXJhdF8xMFgyX29sZCAsaWRlbnQudXNlID0gIm9sZCIgKSApICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQp5b3VuZ19hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24ob2JqZWN0ID0gU2V0SWRlbnQoIG9iamVjdCA9IHNldXJhdF8xMFgyX3lvdW5nICxpZGVudC51c2UgPSAieW91bmciKSApICAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lIikKCmF2ZyA8LSBmdWxsX2pvaW4oeCA9IG9sZF9hdmcgICwgeSA9IHlvdW5nX2F2ZyAgLCBieSA9ICJnZW5lIikKCmF2ZyRvbGQgPC0gYXZnJG9sZCArIDEKYXZnJHlvdW5nIDwtIGF2ZyR5b3VuZyArIDEKCmF2Z19tdXQgPC0gbXV0YXRlKGF2ZyAsIHJ0PW9sZC95b3VuZyApICU+JSBmaWx0ZXIocnQgPiAyICB8IHJ0IDwgMC41KQoKbXggPC0gbWF4KGMoYXZnJG9sZCxhdmckeW91bmcpKSs1CgpnZ3Bsb3QoZGF0YSA9IGF2ZyAsIG1hcHBpbmcgPSBhZXMoeCA9IHlvdW5nICwgeSA9IG9sZCApKSArIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleSIpICsgY29vcmRfZXF1YWwoKSArIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYygxLDEwMDApICwgeSA9IGMoMSwxMDAwKSApICwgbWFwcGluZyA9IGFlcyh4PXgseT15KSAsIGFscGhhID0gMSAsIGNvbG9yID0gImdyZXk0MCIgLCBzaXplPSAwLjMgKSArIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYygyLDEwMDApICwgeSA9IGMoMSw1MDApICkgLCBtYXBwaW5nID0gYWVzKHg9eCx5PXkpICwgYWxwaGEgPSAxICwgY29sb3IgPSAiZ3JleTQwIiAsIGxpbmV0eXBlID0gMiApICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKDEsNTAwKSAsIHkgPSBjKDIsMTAwMCkgKSAsIG1hcHBpbmcgPSBhZXMoeD14LHk9eSkgLCBhbHBoYSA9IDEgLCBjb2xvciA9ICJncmV5NDAiICwgbGluZXR5cGUgPSAyICkgKyBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkgKyBhbm5vdGF0aW9uX2xvZ3RpY2tzKCkgKyBnZW9tX3BvaW50KGRhdGEgPSBhdmdfbXV0ICwgc2l6ZSA9IDIgICwgZmlsbCA9ICJibGFjayIgLCBwY2ggPSAyMSAgKSArIGdndGl0bGUoIkNvbXBhcmlzb24gb2YgYXZlcmFnZSBnZW5lIGV4cHJlc3Npb25cbmJldHdlZW4geW91bmcgYW5kIG9sZCIpCgpgYGAKCiMjIE9ubHkgbGluZWFnZQoKYGBge3IgZmlnLmhlaWdodD01ICwgZmlnLndpZHRoPTV9CmxpYnJhcnkoZ2dyZXBlbCkKCm9sZF9hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24ob2JqZWN0ID0gU2V0SWRlbnQoIG9iamVjdCA9IFN1YnNldERhdGEoIHNldXJhdF8xMFgyX29sZCAsIGlkZW50LnVzZSA9IGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMCIsImFOU0MxIiwiYU5TQzIiLCJUQVAiLCJOQiIpICkgICxpZGVudC51c2UgPSAib2xkIiApICkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZSIpCnlvdW5nX2F2ZyA8LSBBdmVyYWdlRXhwcmVzc2lvbihvYmplY3QgPSBTZXRJZGVudCggb2JqZWN0ID0gU3Vic2V0RGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMl95b3VuZyAsIGlkZW50LnVzZSA9IGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMCIsImFOU0MxIiwiYU5TQzIiLCJUQVAiLCJOQiIpICkgLGlkZW50LnVzZSA9ICJ5b3VuZyIpICkgICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQoKYXZnIDwtIGZ1bGxfam9pbih4ID0gb2xkX2F2ZyAgLCB5ID0geW91bmdfYXZnICAsIGJ5ID0gImdlbmUiKQoKYXZnJG9sZCA8LSBhdmckb2xkICsgMQphdmckeW91bmcgPC0gYXZnJHlvdW5nICsgMQoKYXZnX211dCA8LSBtdXRhdGUoYXZnICwgcnQ9b2xkL3lvdW5nICkgJT4lIGZpbHRlcihydCA+IDIgIHwgcnQgPCAwLjUpCgpteCA8LSBtYXgoYyhhdmckb2xkLGF2ZyR5b3VuZykpKzUKCmdncGxvdChkYXRhID0gYXZnICwgbWFwcGluZyA9IGFlcyh4ID0geW91bmcgLCB5ID0gb2xkICkpICsgZ2VvbV9wb2ludCggY29sb3IgPSAiZ3JleSIgKSArIGNvb3JkX2VxdWFsKCkgKyBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGMoMSwxMDAwKSAsIHkgPSBjKDEsMTAwMCkgKSAsIG1hcHBpbmcgPSBhZXMoeD14LHk9eSkgLCBhbHBoYSA9IDEgLCBjb2xvciA9ICJncmV5NDAiICwgc2l6ZT0gMC4zICkgKyBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGMoMiwxMDAwKSAsIHkgPSBjKDEsNTAwKSApICwgbWFwcGluZyA9IGFlcyh4PXgseT15KSAsIGFscGhhID0gMSAsIGNvbG9yID0gImdyZXk0MCIgLCBsaW5ldHlwZSA9IDIgKSArIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYygxLDUwMCkgLCB5ID0gYygyLDEwMDApICkgLCBtYXBwaW5nID0gYWVzKHg9eCx5PXkpICwgYWxwaGEgPSAxICwgY29sb3IgPSAiZ3JleTQwIiAsIGxpbmV0eXBlID0gMiApICsgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpICsgYW5ub3RhdGlvbl9sb2d0aWNrcygpICsgZ2VvbV9wb2ludChkYXRhID0gYXZnX211dCAsIHNpemUgPSAyICAsIGZpbGwgPSAiYmxhY2siICwgcGNoID0gMjEgICApICsgZ2d0aXRsZSgiQ29tcGFyaXNvbiBvZiBhdmVyYWdlIGdlbmUgZXhwcmVzc2lvblxuYmV0d2VlbiB5b3VuZyBhbmQgb2xkIC0gcU5TQzEgdG8gTkIiKQoKYGBgCgojIyBPbmx5IE5TQ3MKCmBgYHtyIGZpZy5oZWlnaHQ9NSAsIGZpZy53aWR0aD01fQpsaWJyYXJ5KGdncmVwZWwpCgpvbGRfYXZnIDwtIEF2ZXJhZ2VFeHByZXNzaW9uKG9iamVjdCA9IFNldElkZW50KCBvYmplY3QgPSBTdWJzZXREYXRhKCBzZXVyYXRfMTBYMl9vbGQgLCBpZGVudC51c2UgPSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzAiLCJhTlNDMSIsImFOU0MyIikgKSAgLGlkZW50LnVzZSA9ICJvbGQiICkgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lIikKeW91bmdfYXZnIDwtIEF2ZXJhZ2VFeHByZXNzaW9uKG9iamVjdCA9IFNldElkZW50KCBvYmplY3QgPSBTdWJzZXREYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyX3lvdW5nICwgaWRlbnQudXNlID0gYygicU5TQzEiLCJxTlNDMiIsImFOU0MwIiwiYU5TQzEiLCJhTlNDMiIpICkgLGlkZW50LnVzZSA9ICJ5b3VuZyIpICkgICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQoKYXZnIDwtIGZ1bGxfam9pbih4ID0gb2xkX2F2ZyAgLCB5ID0geW91bmdfYXZnICAsIGJ5ID0gImdlbmUiKQoKYXZnJG9sZCA8LSBhdmckb2xkICsgMQphdmckeW91bmcgPC0gYXZnJHlvdW5nICsgMQoKYXZnX211dCA8LSBtdXRhdGUoYXZnICwgcnQ9b2xkL3lvdW5nICkgJT4lIGZpbHRlcihydCA+IDIgIHwgcnQgPCAwLjUpCgpteCA8LSBtYXgoYyhhdmckb2xkLGF2ZyR5b3VuZykpKzUKCmdncGxvdChkYXRhID0gYXZnICwgbWFwcGluZyA9IGFlcyh4ID0geW91bmcgLCB5ID0gb2xkICkpICsgZ2VvbV9wb2ludCggY29sb3IgPSAiZ3JleSIpICsgY29vcmRfZXF1YWwoKSArIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYygxLDEwMDApICwgeSA9IGMoMSwxMDAwKSApICwgbWFwcGluZyA9IGFlcyh4PXgseT15KSAsIGFscGhhID0gMSAsIGNvbG9yID0gImdyZXk0MCIgLCBzaXplPSAwLjMgKSArIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYygyLDEwMDApICwgeSA9IGMoMSw1MDApICkgLCBtYXBwaW5nID0gYWVzKHg9eCx5PXkpICwgYWxwaGEgPSAxICwgY29sb3IgPSAiZ3JleTQwIiAsIGxpbmV0eXBlID0gMiApICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKDEsNTAwKSAsIHkgPSBjKDIsMTAwMCkgKSAsIG1hcHBpbmcgPSBhZXMoeD14LHk9eSkgLCBhbHBoYSA9IDEgLCBjb2xvciA9ICJncmV5NDAiICwgbGluZXR5cGUgPSAyICkgKyBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkgKyBhbm5vdGF0aW9uX2xvZ3RpY2tzKCkgKyBnZW9tX3BvaW50KGRhdGEgPSBhdmdfbXV0ICwgc2l6ZSA9IDIgICwgZmlsbCA9ICJibGFjayIgLCBwY2ggPSAyMSAgICkgKyBnZ3RpdGxlKCJDb21wYXJpc29uIG9mIGF2ZXJhZ2UgZ2VuZSBleHByZXNzaW9uXG5iZXR3ZWVuIHlvdW5nIGFuZCBvbGQgLSBvbmx5IG5ldXJhbCBzdGVtIGNlbGxzIikKCmBgYAoKCiMjIE9ubHkgT2xpZ29kZW5kcm9jeXRlcyBhbmQgT2xpZ29kZW5kcm9jeXRlIHByb2dlbml0b3JzCgpgYGB7ciBmaWcuaGVpZ2h0PTUgLCBmaWcud2lkdGg9NX0KbGlicmFyeShnZ3JlcGVsKQoKb2xkX2F2ZyA8LSBBdmVyYWdlRXhwcmVzc2lvbihvYmplY3QgPSBTZXRJZGVudCggb2JqZWN0ID0gU3Vic2V0RGF0YSggc2V1cmF0XzEwWDJfb2xkICwgaWRlbnQudXNlID0gYygiT0QiLCJPUEMiKSApICAsaWRlbnQudXNlID0gIm9sZCIgKSApICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQp5b3VuZ19hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24ob2JqZWN0ID0gU2V0SWRlbnQoIG9iamVjdCA9IFN1YnNldERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDJfeW91bmcgLCBpZGVudC51c2UgPSBjKCJPRCIsIk9QQyIpICkgLGlkZW50LnVzZSA9ICJ5b3VuZyIpICkgICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQoKYXZnIDwtIGZ1bGxfam9pbih4ID0gb2xkX2F2ZyAgLCB5ID0geW91bmdfYXZnICAsIGJ5ID0gImdlbmUiKQoKYXZnJG9sZCA8LSBhdmckb2xkICsgMQphdmckeW91bmcgPC0gYXZnJHlvdW5nICsgMQoKYXZnX211dCA8LSBtdXRhdGUoYXZnICwgcnQ9b2xkL3lvdW5nICkgJT4lIGZpbHRlcihydCA+IDIgIHwgcnQgPCAwLjUpCgpteCA8LSBtYXgoYyhhdmckb2xkLGF2ZyR5b3VuZykpKzUKCmdncGxvdChkYXRhID0gYXZnICwgbWFwcGluZyA9IGFlcyh4ID0geW91bmcgLCB5ID0gb2xkICkpICsgZ2VvbV9wb2ludChjb2xvciA9ICJncmV5IikgKyBjb29yZF9lcXVhbCgpICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKDEsMTAwMCkgLCB5ID0gYygxLDEwMDApICkgLCBtYXBwaW5nID0gYWVzKHg9eCx5PXkpICwgYWxwaGEgPSAxICwgY29sb3IgPSAiZ3JleTQwIiAsIHNpemU9IDAuMyApICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKDIsMTAwMCkgLCB5ID0gYygxLDUwMCkgKSAsIG1hcHBpbmcgPSBhZXMoeD14LHk9eSkgLCBhbHBoYSA9IDEgLCBjb2xvciA9ICJncmV5NDAiICwgbGluZXR5cGUgPSAyICkgKyBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGMoMSw1MDApICwgeSA9IGMoMiwxMDAwKSApICwgbWFwcGluZyA9IGFlcyh4PXgseT15KSAsIGFscGhhID0gMSAsIGNvbG9yID0gImdyZXk0MCIgLCBsaW5ldHlwZSA9IDIgKSArIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKSArIGFubm90YXRpb25fbG9ndGlja3MoKSArIGdlb21fcG9pbnQoZGF0YSA9IGF2Z19tdXQgLCBzaXplID0gMS41ICAsIGZpbGwgPSAiYmxhY2siICwgcGNoID0gMjEgICApICsgZ2d0aXRsZSgiQ29tcGFyaXNvbiBvZiBhdmVyYWdlIGdlbmUgZXhwcmVzc2lvblxuYmV0d2VlbiB5b3VuZyBhbmQgb2xkIC0gT1BDIGFuZCBPRCIpCmBgYAoKIyMgT25seSBPbGlnb2RlbmRyb2N5dGUgcHJvZ2VuaXRvcnMKCmBgYHtyIGZpZy5oZWlnaHQ9NSAsIGZpZy53aWR0aD01fQpsaWJyYXJ5KGdncmVwZWwpCgpvbGRfYXZnIDwtIEF2ZXJhZ2VFeHByZXNzaW9uKG9iamVjdCA9IFNldElkZW50KCBvYmplY3QgPSBTdWJzZXREYXRhKCBzZXVyYXRfMTBYMl9vbGQgLCBpZGVudC51c2UgPSBjKCJPUEMiKSApICAsaWRlbnQudXNlID0gIm9sZCIgKSApICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQp5b3VuZ19hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24ob2JqZWN0ID0gU2V0SWRlbnQoIG9iamVjdCA9IFN1YnNldERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDJfeW91bmcgLCBpZGVudC51c2UgPSBjKCJPUEMiKSApICxpZGVudC51c2UgPSAieW91bmciKSApICAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lIikKCmF2ZyA8LSBmdWxsX2pvaW4oeCA9IG9sZF9hdmcgICwgeSA9IHlvdW5nX2F2ZyAgLCBieSA9ICJnZW5lIikKCmF2ZyRvbGQgPC0gYXZnJG9sZCArIDEKYXZnJHlvdW5nIDwtIGF2ZyR5b3VuZyArIDEKCmF2Z19tdXQgPC0gbXV0YXRlKGF2ZyAsIHJ0PW9sZC95b3VuZyApICU+JSBmaWx0ZXIocnQgPiAyICB8IHJ0IDwgMC41KQoKbXggPC0gbWF4KGMoYXZnJG9sZCxhdmckeW91bmcpKSs1CgpnZ3Bsb3QoZGF0YSA9IGF2ZyAsIG1hcHBpbmcgPSBhZXMoeCA9IHlvdW5nICwgeSA9IG9sZCApKSArIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleSIpICsgY29vcmRfZXF1YWwoKSArIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYygxLDEwMDApICwgeSA9IGMoMSwxMDAwKSApICwgbWFwcGluZyA9IGFlcyh4PXgseT15KSAsIGFscGhhID0gMSAsIGNvbG9yID0gImdyZXk0MCIgLCBzaXplPSAwLjMgKSArIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh4ID0gYygyLDEwMDApICwgeSA9IGMoMSw1MDApICkgLCBtYXBwaW5nID0gYWVzKHg9eCx5PXkpICwgYWxwaGEgPSAxICwgY29sb3IgPSAiZ3JleTQwIiAsIGxpbmV0eXBlID0gMiApICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKDEsNTAwKSAsIHkgPSBjKDIsMTAwMCkgKSAsIG1hcHBpbmcgPSBhZXMoeD14LHk9eSkgLCBhbHBoYSA9IDEgLCBjb2xvciA9ICJncmV5NDAiICwgbGluZXR5cGUgPSAyICkgKyBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkgKyBhbm5vdGF0aW9uX2xvZ3RpY2tzKCkgKyBnZW9tX3BvaW50KGRhdGEgPSBhdmdfbXV0ICwgc2l6ZSA9IDIgLCBmaWxsID0gImJsYWNrIiAsIHBjaCA9IDIxICAgKSArIGdndGl0bGUoIkNvbXBhcmlzb24gb2YgYXZlcmFnZSBnZW5lIGV4cHJlc3Npb25cbmJldHdlZW4geW91bmcgYW5kIG9sZCAtIE9QQyIpCmBgYAoKIyMgT25seSBPbGlnb2RlbmRyb2N5dGVzCgpgYGB7ciBmaWcuaGVpZ2h0PTUgLCBmaWcud2lkdGg9NX0KbGlicmFyeShnZ3JlcGVsKQoKb2xkX2F2ZyA8LSBBdmVyYWdlRXhwcmVzc2lvbihvYmplY3QgPSBTZXRJZGVudCggb2JqZWN0ID0gU3Vic2V0RGF0YSggc2V1cmF0XzEwWDJfb2xkICwgaWRlbnQudXNlID0gYygiT0QiKSApICAsaWRlbnQudXNlID0gIm9sZCIgKSApICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQp5b3VuZ19hdmcgPC0gQXZlcmFnZUV4cHJlc3Npb24ob2JqZWN0ID0gU2V0SWRlbnQoIG9iamVjdCA9IFN1YnNldERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDJfeW91bmcgLCBpZGVudC51c2UgPSBjKCJPRCIpICkgLGlkZW50LnVzZSA9ICJ5b3VuZyIpICkgICU+JSByb3duYW1lc190b19jb2x1bW4oImdlbmUiKQoKYXZnIDwtIGZ1bGxfam9pbih4ID0gb2xkX2F2ZyAgLCB5ID0geW91bmdfYXZnICAsIGJ5ID0gImdlbmUiKQoKYXZnJG9sZCA8LSBhdmckb2xkICsgMQphdmckeW91bmcgPC0gYXZnJHlvdW5nICsgMQoKYXZnX211dCA8LSBtdXRhdGUoYXZnICwgcnQ9b2xkL3lvdW5nICkgJT4lIGZpbHRlcihydCA+IDIgIHwgcnQgPCAwLjUpCgpteCA8LSBtYXgoYyhhdmckb2xkLGF2ZyR5b3VuZykpKzUKCmdncGxvdChkYXRhID0gYXZnICwgbWFwcGluZyA9IGFlcyh4ID0geW91bmcgLCB5ID0gb2xkICkpICsgZ2VvbV9wb2ludChjb2xvciA9ICJncmV5IikgKyBjb29yZF9lcXVhbCgpICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKDEsMTAwMCkgLCB5ID0gYygxLDEwMDApICkgLCBtYXBwaW5nID0gYWVzKHg9eCx5PXkpICwgYWxwaGEgPSAxICwgY29sb3IgPSAiZ3JleTQwIiAsIHNpemU9IDAuMyApICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKDIsMTAwMCkgLCB5ID0gYygxLDUwMCkgKSAsIG1hcHBpbmcgPSBhZXMoeD14LHk9eSkgLCBhbHBoYSA9IDEgLCBjb2xvciA9ICJncmV5NDAiICwgbGluZXR5cGUgPSAyICkgKyBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGMoMSw1MDApICwgeSA9IGMoMiwxMDAwKSApICwgbWFwcGluZyA9IGFlcyh4PXgseT15KSAsIGFscGhhID0gMSAsIGNvbG9yID0gImdyZXk0MCIgLCBsaW5ldHlwZSA9IDIgKSArIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKSArIGFubm90YXRpb25fbG9ndGlja3MoKSArIGdlb21fcG9pbnQoZGF0YSA9IGF2Z19tdXQgLCBzaXplID0gMiAsIGZpbGwgPSAiYmxhY2siICwgcGNoID0gMjEgICApICsgZ2d0aXRsZSgiQ29tcGFyaXNvbiBvZiBhdmVyYWdlIGdlbmUgZXhwcmVzc2lvblxuYmV0d2VlbiB5b3VuZyBhbmQgb2xkIC0gT0QiKQpgYGAKCiMgRXVjbGlkZWFuIGRpc3RhbmNlcyBiZXR3ZWVuIGNsdXN0ZXJzIHExIGFuZCBxMiwgcTIgYW5kIGEwICwgYTAgYW5kIGExICwgb3BjIGFuZCBvZAoKYGBge3J9CmRhdGFfc2VsZWN0ZWRfY2VsbHR5cGVzIDwtIEZldGNoRGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIHZhcnMuYWxsID0gYygiUEMxIiwiUEMyIiwiUEMzIiwiUEM0IiwiUEM1IiwiUEM2IiwiUEM3IiwiUEM4IikgLCBjZWxscy51c2UgPSBXaGljaENlbGxzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQgPSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzAiLCJhTlNDMSIsImFOU0MyIiwiVEFQIiwiTkIiLCJPUEMiLCJPRCIpKSkKYGBgCgpDYWxjdWxhdGUgZXVjbGlkZWFuIGRpc3RhbmNlCgpgYGB7cn0KZHN0bWF0X3NlbGVjdGVkX2NlbGx0eXBlcyA8LSBhcy5tYXRyaXgoIGRpc3QoZGF0YV9zZWxlY3RlZF9jZWxsdHlwZXMpICkKYGBgCgpWaWV3IGludG8gdGhlIGRpc3RhbmNlIG1hdHJpeAoKYGBge3J9CmhlYWQoZHN0bWF0X3NlbGVjdGVkX2NlbGx0eXBlc1sxOjUsMTo1XSkKYGBgCgpHZXQgdGhlIG5hbWVzIG9mIHRoZSBjZWxscyBmcm9tIHRoZSBkaWZmZXJlbnQgY2VsbHR5cGVzCgpgYGB7cn0KcTEgPC0gV2hpY2hDZWxscyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gYygicU5TQzEiKSkKcTIgPC0gV2hpY2hDZWxscyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gYygicU5TQzIiKSkKYTAgPC0gV2hpY2hDZWxscyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gYygiYU5TQzAiKSkKYTEgPC0gV2hpY2hDZWxscyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gYygiYU5TQzEiKSkKYTIgPC0gV2hpY2hDZWxscyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gYygiYU5TQzIiKSkKdGFwIDwtIFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9IGMoIlRBUCIpKQpuYiAgPC0gV2hpY2hDZWxscyhvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gYygiTkIiKSkKb3BjIDwtIFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9IGMoIk9QQyIpKQpvZCA8LSBXaGljaENlbGxzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQgPSBjKCJPRCIpKQpgYGAKCiMjU2VsZWN0IHRoZSBzdWJtYXRyaXggd2hpY2ggY29udGFpbnMgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIGNlbGx0eXBlcwoKIyMjIHExIGFuZCBxMgoKYGBge3J9CmRzdG1hdF9xMXEyIDwtIGRzdG1hdF9zZWxlY3RlZF9jZWxsdHlwZXNbcTEscTJdCgpoaXN0KGRzdG1hdF9xMXEyICwgeGxhYiA9ICJFdWNsaWRlYW4gRGlzdGFuY2UiICwgbWFpbiA9ICJIaXN0b2dyYW06IEV1Y2xpZGVhbiBEaXN0YW5jZXMgZnJvbSBxTlNDMSB0byBxTlNDMiIgKQphYmxpbmUodiA9IG1lYW4oZHN0bWF0X3ExcTIpICwgY29sID0gInJlZCIpCmBgYAoKIyMjIHEyIGFuZCBhMAoKYGBge3J9CmRzdG1hdF9xMmEwIDwtIGRzdG1hdF9zZWxlY3RlZF9jZWxsdHlwZXNbcTIsYTBdCgpoaXN0KGRzdG1hdF9xMmEwLCB4bGFiID0gIkV1Y2xpZGVhbiBEaXN0YW5jZSIgLCBtYWluID0gIkhpc3RvZ3JhbTogRXVjbGlkZWFuIERpc3RhbmNlcyBmcm9tIHFOU0MyIHRvIGFOU0MwIiApCmFibGluZSh2ID0gbWVhbihkc3RtYXRfcTJhMCkgLCBjb2wgPSAicmVkIikKYGBgCgojIyMgYTAgYW5kIGExCgpgYGB7cn0KZHN0bWF0X2EwYTEgPC0gZHN0bWF0X3NlbGVjdGVkX2NlbGx0eXBlc1thMCxhMV0KCmhpc3QoZHN0bWF0X2EwYTEsIHhsYWIgPSAiRXVjbGlkZWFuIERpc3RhbmNlIiAsIG1haW4gPSAiSGlzdG9ncmFtOiBFdWNsaWRlYW4gRGlzdGFuY2VzIGZyb20gYU5TQzAgdG8gYU5TQzEiICkKYWJsaW5lKHYgPSBtZWFuKGRzdG1hdF9hMGExKSAsIGNvbCA9ICJyZWQiKQpgYGAKCiMjIyBvcGMgYW5kIG9kCgpgYGB7cn0KZHN0bWF0X29wY19vZCA8LSBkc3RtYXRfc2VsZWN0ZWRfY2VsbHR5cGVzW29wYyxvZF0KCmhpc3QoZHN0bWF0X29wY19vZCAsIHhsYWIgPSAiRXVjbGlkZWFuIERpc3RhbmNlIiAsIG1haW4gPSAiSGlzdG9ncmFtOiBFdWNsaWRlYW4gRGlzdGFuY2VzIGZyb20gT1BDIHRvIE9EIiApCmFibGluZSh2ID0gbWVhbihkc3RtYXRfb3BjX29kKSAsIGNvbCA9ICJyZWQiKQpgYGAKCiMjIyBxMSBhbmQgb2QKCmBgYHtyfQpkc3RtYXRfcTFfb2QgPC0gZHN0bWF0X3NlbGVjdGVkX2NlbGx0eXBlc1txMSxvZF0KCmhpc3QoZHN0bWF0X3ExX29kICwgeGxhYiA9ICJFdWNsaWRlYW4gRGlzdGFuY2UiICwgbWFpbiA9ICJIaXN0b2dyYW06IEV1Y2xpZGVhbiBEaXN0YW5jZXMgZnJvbSBxTlNDMSB0byBPRCIgKQphYmxpbmUodiA9IG1lYW4oZHN0bWF0X3ExX29kKSAsIGNvbCA9ICJyZWQiKQpgYGAKCiMjIyBxMiBhbmQgb2QKCmBgYHtyfQpkc3RtYXRfcTJfb2QgPC0gZHN0bWF0X3NlbGVjdGVkX2NlbGx0eXBlc1txMixvZF0KCmhpc3QoZHN0bWF0X3EyX29kICwgeGxhYiA9ICJFdWNsaWRlYW4gRGlzdGFuY2UiICwgbWFpbiA9ICJIaXN0b2dyYW06IEV1Y2xpZGVhbiBEaXN0YW5jZXMgZnJvbSBxTlNDMiB0byBPRCIgKQphYmxpbmUodiA9IG1lYW4oZHN0bWF0X3EyX29kKSAsIGNvbCA9ICJyZWQiKQpgYGAKCiMjIERlbnNpdHkgcGxvdCBvZiBldWNsaWRlYW4gZGlzdGFuY2VzCgpgYGB7ciBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD05fQpkZl9oaXN0PC0gYmluZF9yb3dzKAogIGRhdGEuZnJhbWUoIGNvbXBhcmlzb24gPSAicU5TQzEgdnMgcU5TQzIiICwgY291bnQgPSBhcy52ZWN0b3IoZHN0bWF0X3ExcTIpICkgLAogIGRhdGEuZnJhbWUoIGNvbXBhcmlzb24gPSAicU5TQzIgdnMgYU5TQzAiICwgY291bnQgPSBhcy52ZWN0b3IoZHN0bWF0X3EyYTApICkgLAogIGRhdGEuZnJhbWUoIGNvbXBhcmlzb24gPSAiYU5TQzAgdnMgYU5TQzEiICwgY291bnQgPSBhcy52ZWN0b3IoZHN0bWF0X2EwYTEpICkgLAogIGRhdGEuZnJhbWUoIGNvbXBhcmlzb24gPSAiT1BDIHZzIE9EIiAsIGNvdW50ID0gYXMudmVjdG9yKGRzdG1hdF9vcGNfb2QpICkgLAogIGRhdGEuZnJhbWUoIGNvbXBhcmlzb24gPSAicU5TQzEgdnMgT0QiICwgY291bnQgPSBhcy52ZWN0b3IoZHN0bWF0X3ExX29kKSApICwKICBkYXRhLmZyYW1lKCBjb21wYXJpc29uID0gImFOU0MxIHZzIGFOU0MyIiAsIGNvdW50ID0gYXMudmVjdG9yKGRzdG1hdF9zZWxlY3RlZF9jZWxsdHlwZXNbYTEsYTJdKSApLAogIGRhdGEuZnJhbWUoIGNvbXBhcmlzb24gPSAiYU5TQzIgdnMgVEFQIiAsIGNvdW50ID1hcy52ZWN0b3IoZHN0bWF0X3NlbGVjdGVkX2NlbGx0eXBlc1thMix0YXBdKSApLAogIGRhdGEuZnJhbWUoIGNvbXBhcmlzb24gPSAiVEFQIHZzIE5CIiAsIGNvdW50ID1hcy52ZWN0b3IoZHN0bWF0X3NlbGVjdGVkX2NlbGx0eXBlc1t0YXAsbmJdKSApCikgCgpkZl9oaXN0JGNvbXBhcmlzb24gPC0gZmFjdG9yKGRmX2hpc3QkY29tcGFyaXNvbiAsIGxldmVscyA9IGMoIk9QQyB2cyBPRCIsInFOU0MxIHZzIE9EIiwicU5TQzEgdnMgcU5TQzIiLCJxTlNDMiB2cyBhTlNDMCIsImFOU0MwIHZzIGFOU0MxIiwiYU5TQzEgdnMgYU5TQzIiLCJhTlNDMiB2cyBUQVAiLCJUQVAgdnMgTkIiKSkKCmdncGxvdChkYXRhID0gZGZfaGlzdCApICsgZ2VvbV9kZW5zaXR5KGFlcyh4PWNvdW50LGNvbG9yPWNvbXBhcmlzb24sIGZpbGwgPSBjb21wYXJpc29uKSAsIGFscGhhID0gMC4yNSApICsgeGxhYigiZXVjbGlkZWFuIGRpc3RhbmNlIikgICsgZmFjZXRfd3JhcChmYWNldHMgPSAiY29tcGFyaXNvbiIgLCBuY29sID0gMSkgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MTkuNykKYGBgCgoKIyBQQ0EgcGxvdCBvZiAxMFggZGF0YSAKCmBgYHtyfQpjZWxsdHlwZV9jb2xvcnMgPC0gYyggcU5TQzEgPSAic3RlZWxibHVlIiAsIHFOU0MyID0gInN0ZWVsYmx1ZTEiICwgYU5TQzAgPSAidG9tYXRvIiAsIGFOU0MxID0gInNpZW5uYTEiLCBhTlNDMiA9ICJzaWVubmEzIiAsIFRBUCA9ICJncmVlbiIgLCBOQiA9ICJ5ZWxsb3ciICwgT1BDID0gInBpbmsiICwgT0QgPSAidmlvbGV0IikKCmFnZV9jb2xvcnMgPC0gYyh5b3VuZyA9ICJ5ZWxsb3dncmVlbiIgLCBvbGQgPSAic2xhdGVibHVlIikKYGBgCgpgYGB7cn0Kc2V1cmF0XzEwWDJfYWdlX2lkZW50IDwtIFNldEFsbElkZW50KG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWQgPSAiYWdlIikKYGBgCgoKIyMgQ29sb3JlZCBieSBjZWxsdHlwZQoKQ2FsY3VsYXRlIHBlcmNlbnRhZ2Ugb2YgdmFyaWFuY2UgZm9yIGFsbCBQQ3MKYGBge3J9CnNkIDwtIEdldERpbVJlZHVjdGlvbihvYmplY3QgPSBzZXVyYXRfMTBYMiAsIHJlZHVjdGlvbi50eXBlID0gInBjYSIgLCBzbG90ID0gInNkZXYiKQpwb3YgPC0gcm91bmQoIChzZF4yL3N1bShzZF4yKSkqMTAwICwgZGlnaXRzID0gMSApCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpwY18xXzIgPC0gUENBUGxvdChvYmplY3QgPSBzZXVyYXRfMTBYMiwgMSwgMiAsIHB0LnNpemUgPSAwLjUgLCBkby5yZXR1cm4gPSBUUlVFKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjZWxsdHlwZV9jb2xvcnMgLCBicmVha3MgPSBuYW1lcyhjZWxsdHlwZV9jb2xvcnMpICkgICArIHhsYWIocGFzdGUoIlBDMSAoIixwb3ZbMV0sIiUgKSIpKSArIHlsYWIocGFzdGUoIlBDMiAoIixwb3ZbMl0sICIlICkiKSkgKyBjb29yZF9lcXVhbCgpCgpwY18xXzIKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGNfMV8zIDwtIFBDQVBsb3Qob2JqZWN0ID0gc2V1cmF0XzEwWDIsIDEsIDMgLCBwdC5zaXplID0gMC41ICwgZG8ucmV0dXJuID0gVFJVRSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2VsbHR5cGVfY29sb3JzICwgYnJlYWtzID0gbmFtZXMoY2VsbHR5cGVfY29sb3JzKSApICArIHhsYWIocGFzdGUoIlBDMSAoIixwb3ZbMV0sIiUgKSIpKSArIHlsYWIocGFzdGUoIlBDMyAoIixwb3ZbM10sICIlICkiKSkgICsgY29vcmRfZXF1YWwoKQoKcGNfMV8zCmBgYAoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBjXzJfMyA8LSBQQ0FQbG90KG9iamVjdCA9IHNldXJhdF8xMFgyLCAyLDMgLCBwdC5zaXplID0gMC41ICwgZG8ucmV0dXJuID0gVFJVRSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2VsbHR5cGVfY29sb3JzICwgYnJlYWtzID0gbmFtZXMoY2VsbHR5cGVfY29sb3JzKSApICArIHhsYWIocGFzdGUoIlBDMiAoIixwb3ZbMl0sIiUgKSIpKSArIHlsYWIocGFzdGUoIlBDMyAoIixwb3ZbM10sICIlICkiKSkgICsgY29vcmRfZXF1YWwoKQoKcGNfMl8zCmBgYAoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBjXzFfNCA8LSBQQ0FQbG90KG9iamVjdCA9IHNldXJhdF8xMFgyLCAxLDQgLCBwdC5zaXplID0gMC41LCBkby5yZXR1cm4gPSBUUlVFKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjZWxsdHlwZV9jb2xvcnMgLCBicmVha3MgPSBuYW1lcyhjZWxsdHlwZV9jb2xvcnMpICkgICsgeGxhYihwYXN0ZSgiUEMxICgiLHBvdlsxXSwiJSApIikpICsgeWxhYihwYXN0ZSgiUEM0ICgiLHBvdls0XSwgIiUgKSIpKSAgKyBjb29yZF9lcXVhbCgpCgpwY18xXzQKYGBgCgojIyBDb2xvcmVkIGJ5IGFnZQoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9ClBDQVBsb3Qob2JqZWN0ID0gc2V1cmF0XzEwWDJfYWdlX2lkZW50LCAxLCAyICwgcHQuc2l6ZSA9IDAuNSwgZG8ucmV0dXJuID0gVFJVRSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYWdlX2NvbG9ycyAsIGJyZWFrcyA9IG5hbWVzKGFnZV9jb2xvcnMpICkgICArIHhsYWIocGFzdGUoIlBDMSAoIixwb3ZbMV0sIiUgKSIpKSArIHlsYWIocGFzdGUoIlBDMiAoIixwb3ZbMl0sICIlICkiKSkgICsgY29vcmRfZXF1YWwoKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpwY18xXzNfYWdlIDwtIFBDQVBsb3Qob2JqZWN0ID0gc2V1cmF0XzEwWDJfYWdlX2lkZW50LCAxLCAzICwgcHQuc2l6ZSA9IDAuNSAsIGRvLnJldHVybiA9IFRSVUUpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGFnZV9jb2xvcnMgLCBicmVha3MgPSBuYW1lcyhhZ2VfY29sb3JzKSApICArIHhsYWIocGFzdGUoIlBDMSAoIixwb3ZbMV0sIiUgKSIpKSArIHlsYWIocGFzdGUoIlBDMyAoIixwb3ZbM10sICIlICkiKSkgICsgY29vcmRfZXF1YWwoKQoKcGNfMV8zX2FnZQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpQQ0FQbG90KG9iamVjdCA9IHNldXJhdF8xMFgyX2FnZV9pZGVudCwgMiwgMyAsIHB0LnNpemUgPSAwLjUgLCBkby5yZXR1cm4gPSBUUlVFKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhZ2VfY29sb3JzICwgYnJlYWtzID0gbmFtZXMoYWdlX2NvbG9ycykgKSArIHhsYWIocGFzdGUoIlBDMiAoIixwb3ZbMl0sIiUgKSIpKSArIHlsYWIocGFzdGUoIlBDMyAoIixwb3ZbM10sICIlICkiKSkgICsgY29vcmRfZXF1YWwoKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpQQ0FQbG90KG9iamVjdCA9IHNldXJhdF8xMFgyX2FnZV9pZGVudCwgMSwgNCAsIHB0LnNpemUgPSAwLjUgLCBkby5yZXR1cm4gPSBUUlVFKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBhZ2VfY29sb3JzICwgYnJlYWtzID0gbmFtZXMoYWdlX2NvbG9ycykgKSAgKyB4bGFiKHBhc3RlKCJQQzEgKCIscG92WzFdLCIlICkiKSkgKyB5bGFiKHBhc3RlKCJQQzQgKCIscG92WzRdLCAiJSApIikpICArIGNvb3JkX2VxdWFsKCkKYGBgCiMjIFBDQSBwbG90cyBncmlkCgpgYGB7ciBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTV9CmdyaWQuYXJyYW5nZShwY18xXzMscGNfMV8zX2FnZSxwY18xXzIscGNfMl8zKQpgYGAKCiMgdC1TTkUgcGxvdCBvZiBhbGwgY2VsbHMKCmBgYHtyfQpkZl9wbG90X3RTTkUgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyLCB2YXJzLmFsbCA9IGMoInRTTkVfMSIsInRTTkVfMiIsImFnZSIsImNlbGx0eXBlIikgKSAlPiUgCiAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbGJhcmNvZGUiKQpgYGAKCmBgYHtyIGZpZy53aWR0aCA9IDEwfQpnZ190c25lIDwtIGdncGxvdCgKICAgICAgZGF0YSA9IGRmX3Bsb3RfdFNORSwKICAgICAgbWFwcGluZyA9IGFlcygKICAgICAgICB4ID0gdFNORV8xLAogICAgICAgIHkgPSB0U05FXzIsCiAgICAgICAgZmlsbCA9IGZhY3RvciggY2VsbHR5cGUgLCBsZXZlbHMgPSBuYW1lcyhjZWxsdHlwZV9jb2xvcnMpICkKICAgICAgICApCiAgICAgKSArCiAgZ2VvbV9wb2ludCggcGNoID0gMjEgLCBjb2xvciA9ICJibGFjayIgLCBzaXplID0gMiApICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjZWxsdHlwZV9jb2xvcnMgLCBuYW1lID0gIkFjdGl2YXRpb24gc3RhdGUiICkgKyAgCiAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgY29vcmRfZXF1YWwoKQoKZ2dfdHNuZQpgYGAKCmBgYHtyIGZpZy53aWR0aCA9IDEwfQpnZ190c25lX2FnZSA8LSBnZ3Bsb3QoCiAgICAgIGRhdGEgPSBkZl9wbG90X3RTTkUsCiAgICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgICAgeCA9IHRTTkVfMSwKICAgICAgICB5ID0gdFNORV8yLAogICAgICAgIGZpbGwgPSBmYWN0b3IoIGFnZSAsIGxldmVscyA9IG5hbWVzKGFnZV9jb2xvcnMpICkKICAgICAgICApCiAgICAgKSArCiAgZ2VvbV9wb2ludCggcGNoID0gMjEgLCBjb2xvciA9ICJibGFjayIgLCBzaXplID0gMiApICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhZ2VfY29sb3JzICwgbmFtZSA9ICJBZ2UiICkgKyAgCiAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgCiAgY29vcmRfZXF1YWwoKQoKZ2dfdHNuZV9hZ2UKYGBgCgojIE1ha2UgUENBIGFuZCB0U05FIHBsb3RzIGZvciBlYWNoIHN1YnBvcHVsYXRpb24KCmBgYHtyfQpQQ0FQbG90X3NldXJhdCA8LSBmdW5jdGlvbihvYmplY3QgLCBkaW0xID0gMSwgZGltMiA9IDIgLCBzY2FsZV9wY3NfYnlfc2QgPSBUUlVFICl7CgogIGdnX3BjYSA8LSBQQ0FQbG90KG9iamVjdCA9IG9iamVjdCAsIGRpbS4xID0gZGltMSwgZGltLjIgPSBkaW0yICwgY29scy51c2UgPSBjZWxsdHlwZV9jb2xvcnMgLCBwdC5zaGFwZSA9ICJhZ2UiLCBkby5yZXR1cm4gPSBUUlVFICkKICAKICBzZCA8LSBHZXREaW1SZWR1Y3Rpb24ob2JqZWN0ID0gb2JqZWN0ICwgcmVkdWN0aW9uLnR5cGUgPSAicGNhIiAsIHNsb3QgPSAic2RldiIpCiAgcG92IDwtIHJvdW5kKCAoc2ReMi9zdW0oc2ReMikpKjEwMCAsIGRpZ2l0cyA9IDEgKQogIAogIGlmKHNjYWxlX3Bjc19ieV9zZCl7CiAgICBnZ19wY2EkZGF0YSR4IDwtIGdnX3BjYSRkYXRhJHggKiBzZFtbZGltMV1dCiAgICBnZ19wY2EkZGF0YSR5IDwtIGdnX3BjYSRkYXRhJHkgKiBzZFtbZGltMl1dCiAgfQogIAogIHhsaW1pdHMgPC0gTlVMTAogIHlsaW1pdHMgPC0gTlVMTAogICAgCiAgeF9yYW5nZSA8LSByYW5nZShnZ19wY2EkZGF0YSR4KQogIHhfZGlmZiA8LSB4X3JhbmdlWzJdLXhfcmFuZ2VbMV0gCiAgCiAgeV9yYW5nZSAgPC0gcmFuZ2UoZ2dfcGNhJGRhdGEkeSkKICB5X2RpZmYgPC0geV9yYW5nZVsyXS15X3JhbmdlWzFdIAoKICBpZih4X2RpZmYgPiB5X2RpZmYpewogICAgCiAgICBvZmZzZXQgPC0gKCggeF9kaWZmIC0geV9kaWZmICkgLyAyICkKICAgIAogICAgeV9yYW5nZVsxXSA8LSB5X3JhbmdlWzFdIC0gb2Zmc2V0CiAgICB5X3JhbmdlWzJdIDwtIHlfcmFuZ2VbMl0gKyBvZmZzZXQKCiAgICB5bGltaXRzIDwtIHlfcmFuZ2UKICAgICAgICAKICB9ZWxzZSBpZiggeV9kaWZmID4geF9kaWZmICl7CiAgICAKICAgIG9mZnNldCA8LSAoKCB5X2RpZmYgLSB4X2RpZmYgKSAvIDIgKQogICAgCiAgICB4X3JhbmdlWzFdIDwtIHhfcmFuZ2VbMV0gLSBvZmZzZXQKICAgIHhfcmFuZ2VbMl0gPC0geF9yYW5nZVsyXSArIG9mZnNldAogIAogICAgeGxpbWl0cyA8LSB4X3JhbmdlCiAgICAKICB9CiAgICAKICAgIAogIGdnIDwtIGdncGxvdChkYXRhID0gZ2dfcGNhJGRhdGEgLCBhZXMoeCA9IHggLCB5ID0geSAsIGZpbGwgPSBwdC5zaGFwZSAgLCBzaGFwZSA9IHB0LnNoYXBlKSApICsgZ2VvbV9wb2ludChzaXplID0gMiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9idygpICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSApICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYWdlX2NvbG9ycyAsIGxpbWl0cyA9IG5hbWVzKGFnZV9jb2xvcnMpICwgIG5hbWUgPSAiQWdlIikgKyBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYyhvbGQgPSAyNCAsIHlvdW5nID0gMjEpICwgbGltaXRzID0gbmFtZXMoYWdlX2NvbG9ycykgLCBuYW1lID0gIkFnZSIpICsgY29vcmRfZXF1YWwoKSAgKyB4bGFiKHBhc3RlKCJQQyIsZGltMSwiICgiLHBvdltkaW0xXSwiJSkiKSkgKyB5bGFiKHBhc3RlKCJQQyIsZGltMiwiICgiLHBvdltkaW0yXSwgIiUpIikpICsgZ2d0aXRsZShwYXN0ZTAodW5pcXVlKGdnX3BjYSRkYXRhJGlkZW50KSkgKSArIGd1aWRlcyhjb2xvciA9ICJub25lIikKICAKICBpZighaXMubnVsbCh4bGltaXRzKSl7Z2cgPC0gZ2cgKyB4bGltKCB4bGltaXRzICl9CiAgaWYoIWlzLm51bGwoeWxpbWl0cykpe2dnIDwtIGdnICsgeWxpbSggeWxpbWl0cyApfQogIAogIGdnCn0KYGBgCgpgYGB7cn0KVFNORVBsb3RfU2V1cmF0IDwtIGZ1bmN0aW9uKHggLCB0aXRsZSA9ICIiKXsKCiAgeGxpbWl0cyA8LSBOVUxMCiAgeWxpbWl0cyA8LSBOVUxMCiAgICAKICB4X3JhbmdlIDwtIHJhbmdlKHgkdFNORV8xKQogIHhfZGlmZiA8LSB4X3JhbmdlWzJdLXhfcmFuZ2VbMV0gCiAgCiAgeV9yYW5nZSAgPC0gcmFuZ2UoeCR0U05FXzIpCiAgeV9kaWZmIDwtIHlfcmFuZ2VbMl0teV9yYW5nZVsxXSAKCiAgaWYoeF9kaWZmID4geV9kaWZmKXsKICAgIAogICAgb2Zmc2V0IDwtICgoIHhfZGlmZiAtIHlfZGlmZiApIC8gMiApCiAgICAKICAgIHlfcmFuZ2VbMV0gPC0geV9yYW5nZVsxXSAtIG9mZnNldAogICAgeV9yYW5nZVsyXSA8LSB5X3JhbmdlWzJdICsgb2Zmc2V0CgogICAgeWxpbWl0cyA8LSB5X3JhbmdlCiAgICAgICAgCiAgfWVsc2UgaWYoIHlfZGlmZiA+IHhfZGlmZiApewogICAgCiAgICBvZmZzZXQgPC0gKCggeV9kaWZmIC0geF9kaWZmICkgLyAyICkKICAgIAogICAgeF9yYW5nZVsxXSA8LSB4X3JhbmdlWzFdIC0gb2Zmc2V0CiAgICB4X3JhbmdlWzJdIDwtIHhfcmFuZ2VbMl0gKyBvZmZzZXQKICAKICAgIHhsaW1pdHMgPC0geF9yYW5nZQogIH0KICAgIAogIGdnIDwtIGdncGxvdChkYXRhID0geCAsIG1hcHBpbmcgPSBhZXMoeCA9IHRTTkVfMSAsIHkgPSB0U05FXzIgLCBmaWxsID0gYWdlICAsIHNoYXBlID0gYWdlKSkgKyBnZW9tX3BvaW50KHNpemUgPSAyLCBjb2xvdXIgPSAiYmxhY2siKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoICJvbGQiID0gICJzbGF0ZWJsdWUiLCAieW91bmciID0gInllbGxvd2dyZWVuIikgLCBsYWJlbHMgPSBjKCAib2xkIiAsICJ5b3VuZyIgKSAsIG5hbWUgPSAiQWdlIiApICsgbGFicyggeCA9ICJ0U05FIDEiICwgeSA9ICJ0U05FIDIiICkgKyBjb29yZF9lcXVhbCgpICsgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKCAib2xkIiA9IDI0ICwgInlvdW5nIiA9IDIxKSAsIGxhYmVscyA9IGMoICJvbGQiICwgInlvdW5nIiApICwgbmFtZSA9ICJBZ2UiKSArIGdndGl0bGUoIHRpdGxlICkKICAKICBpZighaXMubnVsbCh4bGltaXRzKSl7Z2cgPC0gZ2cgKyB4bGltKCB4bGltaXRzICl9CiAgaWYoIWlzLm51bGwoeWxpbWl0cykpe2dnIDwtIGdnICsgeWxpbSggeWxpbWl0cyApfQogIAogIGdnCn0KYGBgCgoKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0Kc2V1cmF0XzEwWDJfcU5TQzEgPC0gU3Vic2V0RGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LnVzZSA9ICJxTlNDMSIpCnNldXJhdF8xMFgyX3FOU0MxIDwtIEZpbmRWYXJpYWJsZUdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX3FOU0MxICwgbWVhbi5mdW5jdGlvbiA9IEV4cE1lYW4sIGRpc3BlcnNpb24uZnVuY3Rpb24gPSBMb2dWTVIpCnNldXJhdF8xMFgyX3FOU0MxIDwtIFJ1blBDQShvYmplY3QgPSBzZXVyYXRfMTBYMl9xTlNDMSAsIGRvLnByaW50ID0gRkFMU0UgKQoKcGNhX3ExIDwtIFBDQVBsb3Rfc2V1cmF0KCBvYmplY3QgPSBzZXVyYXRfMTBYMl9xTlNDMSAsIGRpbTEgPSAxLCBkaW0yID0gMiApCgpwY2FfcTEKClBDQVBsb3Rfc2V1cmF0KCBvYmplY3QgPSBzZXVyYXRfMTBYMl9xTlNDMSAsIGRpbTEgPSAyLCBkaW0yID0gMyApCgpQQ190b3A1MGdlbmVzX3FOU0MxPC0gYmluZF9jb2xzKCBsYXBwbHkoIGxpc3QoUEMxID0gMSwgUEMyID0gMiAsIFBDMyA9IDMpICwgIEZVTj1mdW5jdGlvbih4KXtQQ1RvcEdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX3FOU0MxICwgcGMudXNlID0geCAsIG51bS5nZW5lcyA9IDUwICl9ICkgKQoKc2V1cmF0XzEwWDJfcU5TQzEgPC0gUnVuVFNORShvYmplY3QgPSBzZXVyYXRfMTBYMl9xTlNDMSAsIGRpbXMudXNlID0gMTozICwgc2VlZC51c2UgPSAxICkKClRTTkVQbG90KG9iamVjdCA9IHNldXJhdF8xMFgyX3FOU0MxICkgCgoKCnggPC0gRmV0Y2hEYXRhKCBvYmplY3QgPSAgc2V1cmF0XzEwWDJfcU5TQzEgLCB2YXJzLmFsbCA9IGMoInRTTkVfMSIsInRTTkVfMiIsImFnZSIpICkKCmdnX3RzbmVfcU5TQzEgPC0gVFNORVBsb3RfU2V1cmF0KHggPSB4ICwgdGl0bGUgPSAicU5TQzEiKSAKCmdnX3RzbmVfcU5TQzEKYGBgCgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpzZXVyYXRfMTBYMl9xTlNDMiA8LSBTdWJzZXREYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQudXNlID0gInFOU0MyIikKc2V1cmF0XzEwWDJfcU5TQzIgPC0gRmluZFZhcmlhYmxlR2VuZXMob2JqZWN0ID0gc2V1cmF0XzEwWDJfcU5TQzIgLCBtZWFuLmZ1bmN0aW9uID0gRXhwTWVhbiwgZGlzcGVyc2lvbi5mdW5jdGlvbiA9IExvZ1ZNUikKc2V1cmF0XzEwWDJfcU5TQzIgPC0gUnVuUENBKG9iamVjdCA9IHNldXJhdF8xMFgyX3FOU0MyICwgZG8ucHJpbnQgPSBGQUxTRSApCgpwY2FfcTIgPC0gUENBUGxvdF9zZXVyYXQoIG9iamVjdCA9IHNldXJhdF8xMFgyX3FOU0MyICwgZGltMSA9IDEsIGRpbTIgPSAyICkKCnBjYV9xMgoKUENBUGxvdF9zZXVyYXQoIG9iamVjdCA9IHNldXJhdF8xMFgyX3FOU0MyICwgZGltMSA9IDIsIGRpbTIgPSAzICkKClBDX3RvcDUwZ2VuZXNfcU5TQzI8LSBiaW5kX2NvbHMoIGxhcHBseSggbGlzdChQQzEgPSAxLCBQQzIgPSAyICwgUEMzID0gMykgLCAgRlVOPWZ1bmN0aW9uKHgpe1BDVG9wR2VuZXMob2JqZWN0ID0gc2V1cmF0XzEwWDJfcU5TQzIgLCBwYy51c2UgPSB4ICwgbnVtLmdlbmVzID0gNTAgKX0gKSApCgp4IDwtIEZldGNoRGF0YSggb2JqZWN0ID0gIHNldXJhdF8xMFgyX3FOU0MyICwgdmFycy5hbGwgPSBjKCJ0U05FXzEiLCJ0U05FXzIiLCJhZ2UiKSApCgpnZ190c25lX3FOU0MyIDwtIFRTTkVQbG90X1NldXJhdCh4ID0geCAsIHRpdGxlID0gInFOU0MyIikgCgpnZ190c25lX3FOU0MyCmBgYAoKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0Kc2V1cmF0XzEwWDJfYU5TQzAgPC0gU3Vic2V0RGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LnVzZSA9ICJhTlNDMCIpCnNldXJhdF8xMFgyX2FOU0MwIDwtIEZpbmRWYXJpYWJsZUdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX2FOU0MwICwgbWVhbi5mdW5jdGlvbiA9IEV4cE1lYW4sIGRpc3BlcnNpb24uZnVuY3Rpb24gPSBMb2dWTVIpCnNldXJhdF8xMFgyX2FOU0MwIDwtIFJ1blBDQShvYmplY3QgPSBzZXVyYXRfMTBYMl9hTlNDMCAsIGRvLnByaW50ID0gRkFMU0UgKQoKcGNhX2EwIDwtIFBDQVBsb3Rfc2V1cmF0KCBvYmplY3QgPSBzZXVyYXRfMTBYMl9hTlNDMCAsIGRpbTEgPSAxLCBkaW0yID0gMiApCgpwY2FfYTAKClBDQVBsb3Rfc2V1cmF0KCBvYmplY3QgPSBzZXVyYXRfMTBYMl9hTlNDMCAsIGRpbTEgPSAyLCBkaW0yID0gMyApCgpQQ190b3A1MGdlbmVzX2FOU0MwPC0gYmluZF9jb2xzKCBsYXBwbHkoIGxpc3QoUEMxID0gMSwgUEMyID0gMiAsIFBDMyA9IDMpICwgIEZVTj1mdW5jdGlvbih4KXtQQ1RvcEdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX2FOU0MwICwgcGMudXNlID0geCAsIG51bS5nZW5lcyA9IDUwICl9ICkgKQoKeCA8LSBGZXRjaERhdGEoIG9iamVjdCA9ICBzZXVyYXRfMTBYMl9hTlNDMCAsIHZhcnMuYWxsID0gYygidFNORV8xIiwidFNORV8yIiwiYWdlIikgKQoKZ2dfdHNuZV9hTlNDMCA8LSBUU05FUGxvdF9TZXVyYXQoeCA9IHggLCB0aXRsZSA9ICJhTlNDMCIpIAoKZ2dfdHNuZV9hTlNDMApgYGAKCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnNldXJhdF8xMFgyX2FOU0MxIDwtIFN1YnNldERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC51c2UgPSAiYU5TQzEiKQpzZXVyYXRfMTBYMl9hTlNDMSA8LSBGaW5kVmFyaWFibGVHZW5lcyhvYmplY3QgPSBzZXVyYXRfMTBYMl9hTlNDMSAsIG1lYW4uZnVuY3Rpb24gPSBFeHBNZWFuLCBkaXNwZXJzaW9uLmZ1bmN0aW9uID0gTG9nVk1SKQpzZXVyYXRfMTBYMl9hTlNDMSA8LSBSdW5QQ0Eob2JqZWN0ID0gc2V1cmF0XzEwWDJfYU5TQzEgLCBkby5wcmludCA9IEZBTFNFICkKCnBjYV9hMSA8LSBQQ0FQbG90X3NldXJhdCggb2JqZWN0ID0gc2V1cmF0XzEwWDJfYU5TQzEgLCBkaW0xID0gMSwgZGltMiA9IDIgKQoKcGNhX2ExCgpQQ0FQbG90X3NldXJhdCggb2JqZWN0ID0gc2V1cmF0XzEwWDJfYU5TQzEgLCBkaW0xID0gMiwgZGltMiA9IDMgKQoKUENfdG9wNTBnZW5lc19hTlNDMTwtIGJpbmRfY29scyggbGFwcGx5KCBsaXN0KFBDMSA9IDEsIFBDMiA9IDIgLCBQQzMgPSAzKSAsICBGVU49ZnVuY3Rpb24oeCl7UENUb3BHZW5lcyhvYmplY3QgPSBzZXVyYXRfMTBYMl9hTlNDMSAsIHBjLnVzZSA9IHggLCBudW0uZ2VuZXMgPSA1MCApfSApICkKCnggPC0gRmV0Y2hEYXRhKCBvYmplY3QgPSAgc2V1cmF0XzEwWDJfYU5TQzEgLCB2YXJzLmFsbCA9IGMoInRTTkVfMSIsInRTTkVfMiIsImFnZSIpICkKCmdnX3RzbmVfYU5TQzEgPC0gVFNORVBsb3RfU2V1cmF0KHggPSB4ICwgdGl0bGUgPSAiYU5TQzEiKSAKCmdnX3RzbmVfYU5TQzEKYGBgCgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpzZXVyYXRfMTBYMl9hTlNDMiA8LSBTdWJzZXREYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQudXNlID0gImFOU0MyIikKc2V1cmF0XzEwWDJfYU5TQzIgPC0gRmluZFZhcmlhYmxlR2VuZXMob2JqZWN0ID0gc2V1cmF0XzEwWDJfYU5TQzIgLCBtZWFuLmZ1bmN0aW9uID0gRXhwTWVhbiwgZGlzcGVyc2lvbi5mdW5jdGlvbiA9IExvZ1ZNUikKc2V1cmF0XzEwWDJfYU5TQzIgPC0gUnVuUENBKG9iamVjdCA9IHNldXJhdF8xMFgyX2FOU0MyICwgZG8ucHJpbnQgPSBGQUxTRSApCgpwY2FfYTIgPC0gUENBUGxvdF9zZXVyYXQoIG9iamVjdCA9IHNldXJhdF8xMFgyX2FOU0MyICwgZGltMSA9IDEsIGRpbTIgPSAyICkKCnBjYV9hMgoKUENBUGxvdF9zZXVyYXQoIG9iamVjdCA9IHNldXJhdF8xMFgyX2FOU0MyICwgZGltMSA9IDIsIGRpbTIgPSAzICkKClBDX3RvcDUwZ2VuZXNfYU5TQzIgPC0gYmluZF9jb2xzKCBsYXBwbHkoIGxpc3QoUEMxID0gMSwgUEMyID0gMiAsIFBDMyA9IDMpICwgIEZVTj1mdW5jdGlvbih4KXtQQ1RvcEdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX2FOU0MyICwgcGMudXNlID0geCAsIG51bS5nZW5lcyA9IDUwICl9ICkgKQoKeCA8LSBGZXRjaERhdGEoIG9iamVjdCA9ICBzZXVyYXRfMTBYMl9hTlNDMiAsIHZhcnMuYWxsID0gYygidFNORV8xIiwidFNORV8yIiwiYWdlIikgKQoKZ2dfdHNuZV9hTlNDMiA8LSBUU05FUGxvdF9TZXVyYXQoeCA9IHggLCB0aXRsZSA9ICJhTlNDMiIpIAoKZ2dfdHNuZV9hTlNDMgpgYGAKCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnNldXJhdF8xMFgyX1RBUCA8LSBTdWJzZXREYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQudXNlID0gIlRBUCIpCnNldXJhdF8xMFgyX1RBUCA8LSBGaW5kVmFyaWFibGVHZW5lcyhvYmplY3QgPSBzZXVyYXRfMTBYMl9UQVAgLCBtZWFuLmZ1bmN0aW9uID0gRXhwTWVhbiwgZGlzcGVyc2lvbi5mdW5jdGlvbiA9IExvZ1ZNUikKc2V1cmF0XzEwWDJfVEFQIDwtIFJ1blBDQShvYmplY3QgPSBzZXVyYXRfMTBYMl9UQVAgLCBkby5wcmludCA9IEZBTFNFICkKCnBjYV90YXAgPC0gUENBUGxvdF9zZXVyYXQoIG9iamVjdCA9IHNldXJhdF8xMFgyX1RBUCAsIGRpbTEgPSAxLCBkaW0yID0gMiApCgpwY2FfdGFwCgpQQ0FQbG90X3NldXJhdCggb2JqZWN0ID0gc2V1cmF0XzEwWDJfVEFQICwgZGltMSA9IDIsIGRpbTIgPSAzICkKClBDX3RvcDUwZ2VuZXNfVEFQIDwtIGJpbmRfY29scyggbGFwcGx5KCBsaXN0KFBDMSA9IDEsIFBDMiA9IDIgLCBQQzMgPSAzKSAsICBGVU49ZnVuY3Rpb24oeCl7UENUb3BHZW5lcyhvYmplY3QgPSBzZXVyYXRfMTBYMl9UQVAgLCBwYy51c2UgPSB4ICwgbnVtLmdlbmVzID0gNTAgKX0gKSApCgp4IDwtIEZldGNoRGF0YSggb2JqZWN0ID0gIHNldXJhdF8xMFgyX1RBUCAsIHZhcnMuYWxsID0gYygidFNORV8xIiwidFNORV8yIiwiYWdlIikgKQoKZ2dfdHNuZV9UQVAgPC0gVFNORVBsb3RfU2V1cmF0KHggPSB4ICwgdGl0bGUgPSAiVEFQIikgCgpnZ190c25lX1RBUApgYGAKCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnNldXJhdF8xMFgyX05CIDwtIFN1YnNldERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC51c2UgPSAiTkIiKQpzZXVyYXRfMTBYMl9OQiA8LSBGaW5kVmFyaWFibGVHZW5lcyhvYmplY3QgPSBzZXVyYXRfMTBYMl9OQiAsIG1lYW4uZnVuY3Rpb24gPSBFeHBNZWFuLCBkaXNwZXJzaW9uLmZ1bmN0aW9uID0gTG9nVk1SKQpzZXVyYXRfMTBYMl9OQiA8LSBSdW5QQ0Eob2JqZWN0ID0gc2V1cmF0XzEwWDJfTkIgLCBkby5wcmludCA9IEZBTFNFICkKCnBjYV9OQiA8LSBQQ0FQbG90X3NldXJhdCggb2JqZWN0ID0gc2V1cmF0XzEwWDJfTkIgLCBkaW0xID0gMSwgZGltMiA9IDIgKQoKcGNhX05CCgpQQ0FQbG90X3NldXJhdCggb2JqZWN0ID0gc2V1cmF0XzEwWDJfTkIgLCBkaW0xID0gMiwgZGltMiA9IDMgKQoKUENfdG9wNTBnZW5lc19OQiA8LSBiaW5kX2NvbHMoIGxhcHBseSggbGlzdChQQzEgPSAxLCBQQzIgPSAyICwgUEMzID0gMykgLCAgRlVOPWZ1bmN0aW9uKHgpe1BDVG9wR2VuZXMob2JqZWN0ID0gc2V1cmF0XzEwWDJfTkIgLCBwYy51c2UgPSB4ICwgbnVtLmdlbmVzID0gNTAgKX0gKSApCgp4IDwtIEZldGNoRGF0YSggb2JqZWN0ID0gIHNldXJhdF8xMFgyX05CICwgdmFycy5hbGwgPSBjKCJ0U05FXzEiLCJ0U05FXzIiLCJhZ2UiKSApCgpnZ190c25lX05CIDwtIFRTTkVQbG90X1NldXJhdCh4ID0geCAsIHRpdGxlID0gIk5CIikgCgpnZ190c25lX05CCmBgYAoKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0Kc2V1cmF0XzEwWDJfT1BDIDwtIFN1YnNldERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC51c2UgPSAiT1BDIikKc2V1cmF0XzEwWDJfT1BDIDwtIEZpbmRWYXJpYWJsZUdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX09QQyAsIG1lYW4uZnVuY3Rpb24gPSBFeHBNZWFuLCBkaXNwZXJzaW9uLmZ1bmN0aW9uID0gTG9nVk1SKQpzZXVyYXRfMTBYMl9PUEMgPC0gUnVuUENBKG9iamVjdCA9IHNldXJhdF8xMFgyX09QQyAsIGRvLnByaW50ID0gRkFMU0UgKQoKcGNhX09QQyA8LSBQQ0FQbG90X3NldXJhdCggb2JqZWN0ID0gc2V1cmF0XzEwWDJfT1BDICwgZGltMSA9IDEsIGRpbTIgPSAyICkKCnBjYV9PUEMKClBDQVBsb3Rfc2V1cmF0KCBvYmplY3QgPSBzZXVyYXRfMTBYMl9PUEMgLCBkaW0xID0gMiwgZGltMiA9IDMgKQoKUENfdG9wNTBnZW5lc19PUEMgPC0gYmluZF9jb2xzKCBsYXBwbHkoIGxpc3QoUEMxID0gMSwgUEMyID0gMiAsIFBDMyA9IDMpICwgIEZVTj1mdW5jdGlvbih4KXtQQ1RvcEdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX09QQyAsIHBjLnVzZSA9IHggLCBudW0uZ2VuZXMgPSA1MCApfSApICkKCnggPC0gRmV0Y2hEYXRhKCBvYmplY3QgPSAgc2V1cmF0XzEwWDJfT1BDICwgdmFycy5hbGwgPSBjKCJ0U05FXzEiLCJ0U05FXzIiLCJhZ2UiKSApCgpnZ190c25lX09QQyA8LSBUU05FUGxvdF9TZXVyYXQoeCA9IHggLCB0aXRsZSA9ICJPUEMiKSAKCmdnX3RzbmVfT1BDCmBgYAoKYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0Kc2V1cmF0XzEwWDJfT0QgPC0gU3Vic2V0RGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50LnVzZSA9ICJPRCIpCnNldXJhdF8xMFgyX09EIDwtIEZpbmRWYXJpYWJsZUdlbmVzKG9iamVjdCA9IHNldXJhdF8xMFgyX09EICwgbWVhbi5mdW5jdGlvbiA9IEV4cE1lYW4sIGRpc3BlcnNpb24uZnVuY3Rpb24gPSBMb2dWTVIpCnNldXJhdF8xMFgyX09EIDwtIFJ1blBDQShvYmplY3QgPSBzZXVyYXRfMTBYMl9PRCAsIGRvLnByaW50ID0gRkFMU0UgKQoKcGNhX09EIDwtIFBDQVBsb3Rfc2V1cmF0KCBvYmplY3QgPSBzZXVyYXRfMTBYMl9PRCAsIGRpbTEgPSAxLCBkaW0yID0gMiApCgpwY2FfT0QKClBDQVBsb3Rfc2V1cmF0KCBvYmplY3QgPSBzZXVyYXRfMTBYMl9PRCAsIGRpbTEgPSAyLCBkaW0yID0gMyApCgpQQ190b3A1MGdlbmVzX09EIDwtIGJpbmRfY29scyggbGFwcGx5KCBsaXN0KFBDMSA9IDEsIFBDMiA9IDIgLCBQQzMgPSAzKSAsICBGVU49ZnVuY3Rpb24oeCl7UENUb3BHZW5lcyhvYmplY3QgPSBzZXVyYXRfMTBYMl9PRCAsIHBjLnVzZSA9IHggLCBudW0uZ2VuZXMgPSA1MCApfSApICkKCnggPC0gRmV0Y2hEYXRhKCBvYmplY3QgPSAgc2V1cmF0XzEwWDJfT0QgLCB2YXJzLmFsbCA9IGMoInRTTkVfMSIsInRTTkVfMiIsImFnZSIpICkKCmdnX3RzbmVfT0QgPC0gVFNORVBsb3RfU2V1cmF0KHggPSB4ICwgdGl0bGUgPSAiT0QiKSAKCmdnX3RzbmVfT0QKYGBgCgojIyBHYXRoZXIgYWxsIFBDQSBwbG90cwoKYGBge3IgZmlnLmhlaWdodD0yMCAsIGZpZy53aWR0aD0yMH0KZ3JpZC5hcnJhbmdlKHBjYV9xMSxwY2FfcTIscGNhX2EwLHBjYV9hMSxwY2FfYTIscGNhX3RhcCxwY2FfTkIscGNhX09QQyxwY2FfT0QpCmBgYAoKCiMjIEdhdGhlciBhbGwgdC1TTkUgcGxvdHMKCmBgYHtyIGZpZy5oZWlnaHQ9MjAgLCBmaWcud2lkdGg9MjB9CmdyaWQuYXJyYW5nZShnZ190c25lX3FOU0MxICwgZ2dfdHNuZV9xTlNDMiAsIGdnX3RzbmVfYU5TQzAgLCBnZ190c25lX2FOU0MxICwgZ2dfdHNuZV9hTlNDMiAsIGdnX3RzbmVfVEFQICwgZ2dfdHNuZV9OQiAsIGdnX3RzbmVfT1BDICwgZ2dfdHNuZV9PRCApCmBgYAoKIyBFdWNsaWRlYW4gRGlzdGFuY2VzIGJldHdlZW4geW91bmcgYW5kIG9sZCBjZWxscwoKV2hpY2ggY2VsbHR5cGVzIGRvIHdlIGhhdmU/CgpgYGB7cn0KY2VsbHR5cGVzIDwtIG5hbWVzKGNlbGx0eXBlX2NvbG9ycykKCm5hbWVzKGNlbGx0eXBlcykgPC0gY2VsbHR5cGVzIApgYGAKCkV4dHJhY3QgdGhlIGNlbGwgYW5ub3RhdGlvbiBmcm9tIHRoZSBzZXVyYXQgb2JqZWN0CgpgYGB7cn0KYW5ubyA8LSBGZXRjaERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCB2YXJzLmFsbCA9IGMoImFnZSIsImNlbGx0eXBlIikgKQpgYGAKCgpNYWtlIGEgbGlzdCBvZiBtYXRyaWNlcyB3aXRoIGV4cHJlc3Npb24gZGF0YSBwZXIgY2VsbHR5cGUKCmBgYHtyfQpkYXRhX21hdHJpeF9jZWxsdHlwZXMgPC0gbGFwcGx5KCBYID0gY2VsbHR5cGVzLCBGVU4gPSBmdW5jdGlvbih4KXsKICAKICAgICAgICAgICAgICAgICAgICAgICAgIyBkYXRhXzEwWDJfY2VsbHR5cGUgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgdmFycy5hbGwgPSByb3duYW1lcyhzZXVyYXRfMTBYMkBkYXRhKSAsIGNlbGxzLnVzZSA9IFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9IHgpICkKICAKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YV8xMFgyX2NlbGx0eXBlIDwtIEZldGNoRGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIHZhcnMuYWxsID0gYygiUEMxIiwiUEMyIiwiUEMzIiwiUEM0IiwiUEM1IiwiUEM2IiwiUEM3IiwiUEM4IikgLCBjZWxscy51c2UgPSBXaGljaENlbGxzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQgPSB4KSAgKQogICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgZGF0YV8xMFgyX2NlbGx0eXBlCiAgICAgICAgICAgICAgICAgICAgICB9KQpgYGAKCk5vdyB3ZSBjYWxjdWxhdGUgdGhlIGV1Y2xpZGVhbiBkaXN0YW5jZSBiZXR3ZWVuIGFsbCB0aGUgc2FtcGxlcyBmcm9tIG9uZSBjZWxsdHlwZQoKYGBge3J9CmV1Y19kaXN0X2NlbGx0eXBlcyA8LSBsYXBwbHkoIGRhdGFfbWF0cml4X2NlbGx0eXBlcyAsIEZVTiA9IGZ1bmN0aW9uKHgpe2FzLm1hdHJpeChkaXN0KHggPSB4LCBtZXRob2QgPSAiZXVjbGlkZWFuIiAsIHVwcGVyID0gVFJVRSkpfSApCmBgYAoKIyMgRGVuc2l0eSBvZiBldWNsaWRlYW4gZGlzdGFuY2VzCgpgYGB7cn0KZGYucGxvdC5saXN0IDwtIGxhcHBseSggWCA9IGV1Y19kaXN0X2NlbGx0eXBlcywgRlVOID0gZnVuY3Rpb24oeCl7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSByb3duYW1lc190b19jb2x1bW4oImNlbGwxIikgJT4lIGdhdGhlcihrZXkgPSAiY2VsbDIiICwgdmFsdWUgPSAiZXVjbGlkZWFuX2Rpc3RhbmNlIiAsIC1jZWxsMSkgJT4lIGxlZnRfam9pbiggeSA9IGRwbHlyOjpyZW5hbWUoYW5ubywgYWdlX2NlbGwxID0gYWdlKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCB2YXIgPSAiY2VsbDEiKSAsIGJ5ID0gImNlbGwxIiApICU+JSBsZWZ0X2pvaW4oIHkgPSBkcGx5cjo6cmVuYW1lKGFubm8sIGFnZV9jZWxsMiA9IGFnZSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbiggdmFyID0gImNlbGwyIikgLCBieSA9ICJjZWxsMiIgKSAlPiUgbXV0YXRlKGNvbXBhcmlzb24gPSBwYXN0ZShhZ2VfY2VsbDEgLCBhZ2VfY2VsbDIgLCBzZXAgPSAiXyIpKQp9KQpgYGAKCmBgYHtyfQpnZy5saXN0IDwtIGxhcHBseSggWCA9IGRmLnBsb3QubGlzdCwgRlVOID0gZnVuY3Rpb24oeCl7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90KGRhdGEgPSB4LCBtYXBwaW5nID0gYWVzKHggPSBldWNsaWRlYW5fZGlzdGFuY2UgLCBjb2xvciA9IGNvbXBhcmlzb24pKSArIGdlb21fZGVuc2l0eSgpICsgZ2d0aXRsZSggcGFzdGUwKHVuaXF1ZShhcy5jaGFyYWN0ZXIoeCRjZWxsdHlwZS54KSkpICkKfSkKYGBgCgojIyMgcU5TQzEKCmBgYHtyfQpwcmludChnZy5saXN0W1sxXV0pCmBgYAoKIyMjIHFOU0MyCgpgYGB7cn0KcHJpbnQoZ2cubGlzdFtbMl1dKQpgYGAKCiMjIyBhTlNDMAoKYGBge3J9CnByaW50KGdnLmxpc3RbWzNdXSkKYGBgCgojIyMgYU5TQzEKCmBgYHtyfQpwcmludChnZy5saXN0W1s0XV0pCmBgYAoKIyMjIGFOU0MyCgpgYGB7cn0KcHJpbnQoZ2cubGlzdFtbNV1dKQpgYGAKCiMjIyBUQVAKCmBgYHtyfQpwcmludChnZy5saXN0W1s2XV0pCmBgYAoKIyMjIE5CCgpgYGB7cn0KcHJpbnQoZ2cubGlzdFtbN11dKQpgYGAKCiMjIyBPUEMKCmBgYHtyfQpwcmludChnZy5saXN0W1s4XV0pCmBgYAoKIyMjIE9ECgpgYGB7cn0KcHJpbnQoZ2cubGlzdFtbOV1dKQpgYGAKCiMgRXVjbGlkZWFuIERpc3RhbmNlcyBiZXR3ZWVuIGNlbGx0eXBlcwoKYGBge3J9CmNlbGx0eXBlX2NvbXBhcmlzb25zIDwtIGxpc3QoIHFOU0MxX3FOU0MyID0gYygicU5TQzEiLCJxTlNDMiIpICwgcU5TQzJfYU5TQzAgPSBjKCJxTlNDMiIsImFOU0MwIikgLCBhTlNDMF9hTlNDMSA9IGMoImFOU0MwIiwiYU5TQzEiKSAsYU5TQzFfYU5TQzIgPSBjKCJhTlNDMSIsImFOU0MyIikgLGFOU0MyX1RBUCA9IGMoImFOU0MyIiwiVEFQIikgLCBhTlNDMl9UQVAgPSBjKCJUQVAiLCJOQiIpICwgcU5TQzFfT0QgPSBjKCJxTlNDMSIsIk9EIikgLCBxTlNDMV9hTlNDMiA9IGMoInFOU0MxIiwiYU5TQzIiKSAgKQoKY2VsbHR5cGVfY29tcGFyaXNvbnMKYGBgCgpNYWtlIGEgbWF0cml4IG9mIHRoZSBkYXRhCgpgYGB7cn0KZGF0YV9tYXRyaXhfbGluZWFnZSA8LSBsYXBwbHkoIFggPSBjZWxsdHlwZV9jb21wYXJpc29ucywgRlVOID0gZnVuY3Rpb24oeCl7CiAgCiAgICAgICAgICAgICAgICAgICAgICAgICMgZGF0YV8xMFgyX2NlbGx0eXBlIDwtIEZldGNoRGF0YShvYmplY3QgPSBzZXVyYXRfMTBYMiAsIHZhcnMuYWxsID0gcm93bmFtZXMoc2V1cmF0XzEwWDJAZGF0YSkgLCBjZWxscy51c2UgPSBXaGljaENlbGxzKG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQgPSB4KSApCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhXzEwWDJfY2VsbHR5cGUgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgdmFycy5hbGwgPSBjKCJQQzEiLCJQQzIiLCJQQzMiLCJQQzQiLCJQQzUiLCJQQzYiLCJQQzciLCJQQzgiKSAsIGNlbGxzLnVzZSA9IFdoaWNoQ2VsbHMob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9IHgpICkgICAgICAgICAgICAgICAgICAgICAgCiAgCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFfMTBYMl9jZWxsdHlwZQogICAgICAgICAgICAgICAgICAgICAgfSkKYGBgCgpOb3cgd2UgY2FsY3VsYXRlIHRoZSBldWNsaWRlYW4gZGlzdGFuY2UgYmV0d2VlbiBhbGwgdGhlIGNlbGxzIGZyb20gdGhlIGNvbXBhcmlzb24KCmBgYHtyfQpldWNfZGlzdF9saW5lYWdlX2FsbCA8LSBsYXBwbHkoIGRhdGFfbWF0cml4X2xpbmVhZ2UgLCBGVU4gPSBmdW5jdGlvbih4KXthcy5tYXRyaXgoZGlzdCh4ID0geCwgbWV0aG9kID0gImV1Y2xpZGVhbiIgLCB1cHBlciA9IFRSVUUpKX0gKQpgYGAKCgojIyMgRGVuc2l0eSBvZiBldWNsaWRlYW4gZGlzdGFuY2VzCgpgYGB7cn0KZGYucGxvdC5saXN0X2FsbCA8LSBsYXBwbHkoIFggPSBldWNfZGlzdF9saW5lYWdlX2FsbCwgRlVOID0gZnVuY3Rpb24oeCl7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSByb3duYW1lc190b19jb2x1bW4oImNlbGwxIikgJT4lIGdhdGhlcihrZXkgPSAiY2VsbDIiICwgdmFsdWUgPSAiZXVjbGlkZWFuX2Rpc3RhbmNlIiAsIC1jZWxsMSkgJT4lIGxlZnRfam9pbiggeSA9IGRwbHlyOjpyZW5hbWUoYW5ubywgYWdlX2NlbGwxID0gYWdlKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCB2YXIgPSAiY2VsbDEiKSAsIGJ5ID0gImNlbGwxIiApICU+JSBsZWZ0X2pvaW4oIHkgPSBkcGx5cjo6cmVuYW1lKGFubm8sIGFnZV9jZWxsMiA9IGFnZSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbiggdmFyID0gImNlbGwyIikgLCBieSA9ICJjZWxsMiIgKSAlPiUgbXV0YXRlKGNvbXBhcmlzb24gPSBwYXN0ZShjZWxsdHlwZS54ICwgY2VsbHR5cGUueSAsIHNlcCA9ICJfIikpCn0pCmBgYAoKCmBgYHtyfQpnZy5saXN0X2FsbCA8LSBsYXBwbHkoIFggPSBkZi5wbG90Lmxpc3RfYWxsLCBGVU4gPSBmdW5jdGlvbih4KXsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QoZGF0YSA9IHgsIG1hcHBpbmcgPSBhZXMoeCA9IGV1Y2xpZGVhbl9kaXN0YW5jZSAsIGNvbG9yID0gY29tcGFyaXNvbikpICsgZ2VvbV9kZW5zaXR5KCkgKyBnZ3RpdGxlKCAiIiApCn0pCmBgYAoKYGBge3J9CnByaW50KGdnLmxpc3RfYWxsW1sxXV0pCmBgYAoKYGBge3J9CnByaW50KGdnLmxpc3RfYWxsW1syXV0pCmBgYAoKYGBge3J9CnByaW50KGdnLmxpc3RfYWxsW1szXV0pCmBgYAoKYGBge3J9CnByaW50KGdnLmxpc3RfYWxsW1s0XV0pCmBgYAoKYGBge3J9CnByaW50KGdnLmxpc3RfYWxsW1s1XV0pCmBgYAoKCiMgRXVjbGlkZWFuIGRpc3RhbmNlcyB5b3VuZyB2cyBvbGQgd2l0aCBxMV9xMiBhcyByZWZlcmVuY2UKCmBgYHtyfQpxMV9xMl9ldWNfZGlzdCA8LSBkZi5wbG90Lmxpc3RfYWxsJHFOU0MxX3FOU0MyICU+JSBmaWx0ZXIoY29tcGFyaXNvbiA9PSAicU5TQzFfcU5TQzIiKQpgYGAKCgpgYGB7cn0KZGYucGxvdC5saXN0X3JlZiA8LSBsYXBwbHkoIFggPSBldWNfZGlzdF9jZWxsdHlwZXMsIEZVTiA9IGZ1bmN0aW9uKHgpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbDEiKSAlPiUgZ2F0aGVyKGtleSA9ICJjZWxsMiIgLCB2YWx1ZSA9ICJldWNsaWRlYW5fZGlzdGFuY2UiICwgLWNlbGwxKSAlPiUgbGVmdF9qb2luKCB5ID0gZHBseXI6OnJlbmFtZShhbm5vLCBhZ2VfY2VsbDEgPSBhZ2UpICU+JSByb3duYW1lc190b19jb2x1bW4oIHZhciA9ICJjZWxsMSIpICwgYnkgPSAiY2VsbDEiICkgJT4lIGxlZnRfam9pbiggeSA9IGRwbHlyOjpyZW5hbWUoYW5ubywgYWdlX2NlbGwyID0gYWdlKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCB2YXIgPSAiY2VsbDIiKSAsIGJ5ID0gImNlbGwyIiApICU+JSBtdXRhdGUoY29tcGFyaXNvbiA9IHBhc3RlKGFnZV9jZWxsMSAsIGFnZV9jZWxsMiAsIHNlcCA9ICJfIikpICU+JSBkcGx5cjo6bXV0YXRlKHR5cGUgPSBhcy5jaGFyYWN0ZXIoY2VsbHR5cGUueCkgKSAlPiUgZHBseXI6OnNlbGVjdCggY2VsbDEsY2VsbDIsZXVjbGlkZWFuX2Rpc3RhbmNlLGNvbXBhcmlzb24gLCB0eXBlKSAlPiUgYmluZF9yb3dzKHExX3EyX2V1Y19kaXN0ICU+JSBkcGx5cjo6bXV0YXRlKHR5cGUgPSBhcy5jaGFyYWN0ZXIoY2VsbHR5cGUueCkgKSAlPiUgZHBseXI6OnNlbGVjdCggY2VsbDEsY2VsbDIsZXVjbGlkZWFuX2Rpc3RhbmNlLGNvbXBhcmlzb24sdHlwZSkpICU+JSBkcGx5cjo6ZmlsdGVyKGNvbXBhcmlzb24gJWluJSBjKCJvbGRfb2xkIiwieW91bmdfb2xkIiwieW91bmdfeW91bmciLCJxTlNDMV9xTlNDMiIpKSAlPiUgbXV0YXRlKGNvbXBhcmlzb24gPSBmYWN0b3IoY29tcGFyaXNvbiAsIGxldmVscyA9IGMoIm9sZF9vbGQiLCJ5b3VuZ19vbGQiLCJ5b3VuZ195b3VuZyIsInFOU0MxX3FOU0MyIikpKQp9KQpgYGAKCmBgYHtyfQpjb2xvcnNfY29tcGFyaXNvbnMgPC0gYygib2xkX29sZCIgPSAic2xhdGVibHVlIiAsInlvdW5nX29sZCIgPSAiZGVlcHNreWJsdWU0IiwieW91bmdfeW91bmciID0gIm9saXZlZHJhYiIsInFOU0MxX3FOU0MyIiA9ICJibGFjayIpCgpnZy5saXN0IDwtIGxhcHBseSggWCA9IGRmLnBsb3QubGlzdF9yZWYsIEZVTiA9IGZ1bmN0aW9uKHgpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdChkYXRhID0geCwgbWFwcGluZyA9IGFlcyh4ID0gZXVjbGlkZWFuX2Rpc3RhbmNlICwgY29sb3IgPSBjb21wYXJpc29uICwgZmlsbCA9IGNvbXBhcmlzb24gKSkgKyBnZW9tX2RlbnNpdHkoIGFscGhhID0gMC4zKSArIGdndGl0bGUoIHBhc3RlMCh1bmlxdWUoYXMuY2hhcmFjdGVyKHgkdHlwZS54KSkpICkgKyB4bGFiKCJFdWNsaWRlYW4gRGlzdGFuY2UiKSArIHlsYWIoIkRlbnNpdHkiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yc19jb21wYXJpc29ucykgKyBzY2FsZV9jb2xvcl9tYW51YWwoIHZhbHVlcyA9IGNvbG9yc19jb21wYXJpc29ucyAgKSArIGxhYnMoY29sb3I9IkNvbXBhcmlzb24iLGZpbGw9IkNvbXBhcmlzb24iKSArIGdndGl0bGUoIHBhc3RlMCh1bmlxdWUoYXMuY2hhcmFjdGVyKHgkdHlwZSkpKSApCn0pCmBgYAoKCiMjIyBxTlNDMQoKYGBge3IgZmlnLndpZHRoPTYsZmlnLmhlaWdodD00fQpwcmludChnZy5saXN0W1sxXV0pCmBgYAoKIyMjIHFOU0MyCgpgYGB7ciBmaWcud2lkdGg9NixmaWcuaGVpZ2h0PTR9CnByaW50KGdnLmxpc3RbWzJdXSkKYGBgCgojIyMgYU5TQzAKCmBgYHtyIGZpZy53aWR0aD02LGZpZy5oZWlnaHQ9NH0KcHJpbnQoZ2cubGlzdFtbM11dKQpgYGAKCiMjIyBhTlNDMQoKYGBge3IgZmlnLndpZHRoPTYsZmlnLmhlaWdodD00fQpwcmludChnZy5saXN0W1s0XV0pCmBgYAoKIyMjIGFOU0MyCgpgYGB7ciBmaWcud2lkdGg9NixmaWcuaGVpZ2h0PTR9CnByaW50KGdnLmxpc3RbWzVdXSkKYGBgCgojIyMgVEFQCgpgYGB7ciBmaWcud2lkdGg9NixmaWcuaGVpZ2h0PTR9CnByaW50KGdnLmxpc3RbWzZdXSkKYGBgCgojIyMgTkIKCmBgYHtyIGZpZy53aWR0aD02LGZpZy5oZWlnaHQ9NH0KcHJpbnQoZ2cubGlzdFtbN11dKQpgYGAKCiMjIyBPUEMKCmBgYHtyIGZpZy53aWR0aD02LGZpZy5oZWlnaHQ9NH0KcHJpbnQoZ2cubGlzdFtbOF1dKQpgYGAKCiMjIyBPRAoKYGBge3IgZmlnLndpZHRoPTYsZmlnLmhlaWdodD00fQpwcmludChnZy5saXN0W1s5XV0pCmBgYAoKIyBFdWNsaWRlYW4gZGlzdGFuY2VzIGJldHdlZW4geW91bmcgYW5kIG9sZCBpbiBQc2V1ZG90aW1lIHdpbmRvdwoKRmlyc3Qgd2Ugc3Vic2V0IG91ciBkYXRhIHRvIGNvbnRhaW4gb25seSB0aGUgTlNDcyBpbiBpbmRpdmlkdWFsIHRhYmxlcyBmb3IgeW91bmcgYW5kIG9sZC4KCmBgYHtyfQpkYXRhXzEwX05TQ3MgPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgdmFycy5hbGwgPSBjKCJQQzEiLCJQQzIiLCJQQzMiLCJQQzQiLCJQQzUiLCJQQzYiLCJQQzciLCJQQzgiKSAsIGNlbGxzLnVzZSA9IFdoaWNoQ2VsbHMoIG9iamVjdCA9IHNldXJhdF8xMFgyICwgaWRlbnQucmVtb3ZlID0gYygiT1BDIiwiT0QiLCJOQiIsIlRBUCIpICkgICkKCm1ldGFfZGF0YV90YWJsZSA8LSBGZXRjaERhdGEob2JqZWN0ID0gc2V1cmF0XzEwWDIgLCB2YXJzLmFsbCA9IGMoImFnZSIsIlBzZXVkb3RpbWUiLCJjZWxsdHlwZSIpICwgY2VsbHMudXNlID0gV2hpY2hDZWxscyggb2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudC5yZW1vdmUgPSBjKCJPUEMiLCJPRCIsIk5CIiwiVEFQIikgKSAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikKYGBgCgpgYGB7cn0KZXVjbF9kaXN0YW5jZXNfYWxsX05TQ3MgPC0gYXMubWF0cml4KCBkaXN0KGRhdGFfMTBfTlNDcyAsIG1ldGhvZCA9ICJldWNsaWRlYW4iICwgdXBwZXIgPSBUUlVFKSApCmBgYAoKYGBge3J9CnBzZXVkb3RpbWVfZGlmZiA9IDIuNQoKZGF0YV9ldWNsaWRlYW5fZGlzdGFuY2VzIDwtIGxhcHBseSggWCA9IHJvd25hbWVzKGRhdGFfMTBfTlNDcykgICwgRlVOID0gZnVuY3Rpb24oeCl7CiAgICByZXF1aXJlKGRwbHlyKQogIAogICAgcHNldWRvdGltZV92YWwgPC0gbWV0YV9kYXRhX3RhYmxlICU+JSBmaWx0ZXIoIGNlbGwgPT0geCAgKSAlPiUgcHVsbCgiUHNldWRvdGltZSIpCiAgCiAgICBhZ2VfY2VsbCA8LSBtZXRhX2RhdGFfdGFibGUgJT4lIGZpbHRlciggY2VsbCA9PSB4ICApICU+JSBwdWxsKCJhZ2UiKQogIAogICAgcHNldWRvdGltZV92YWxzIDwtIGMoIG1pbiA9IHBzZXVkb3RpbWVfdmFsIC0gcHNldWRvdGltZV9kaWZmICwgdmFsdWUgPSBwc2V1ZG90aW1lX3ZhbCAgLCBtYXggPSBwc2V1ZG90aW1lX3ZhbCArIHBzZXVkb3RpbWVfZGlmZiApIAogICAgCiAgICBjZWxsczJjb21wYXJlIDwtIG1ldGFfZGF0YV90YWJsZSAlPiUgZmlsdGVyKCBQc2V1ZG90aW1lIDwgcHNldWRvdGltZV92YWxzWyJtYXgiXSAgJiAgUHNldWRvdGltZSA+IHBzZXVkb3RpbWVfdmFsc1sibWluIl0gKSAKICAgIAogICAgY2VsbHMyY29tcGFyZV95b3VuZyA8LSBjZWxsczJjb21wYXJlICU+JSBmaWx0ZXIoIGFnZSA9PSAieW91bmciICYgY2VsbCAhPSB4ICkgJT4lIHB1bGwoImNlbGwiKSAKCiAgICBjZWxsczJjb21wYXJlX29sZCA8LSBjZWxsczJjb21wYXJlICU+JSBmaWx0ZXIoIGFnZSA9PSAib2xkIiAmIGNlbGwgIT0geCApICU+JSBwdWxsKCJjZWxsIikKICAgICAgICAKICBpZihhZ2VfY2VsbCA9PSAieW91bmciKXsKICAgIHlvdW5nX3RvX3lvdW5nIDwtIGRhdGEuZnJhbWUoIGV1Y2xpZGVhbl9kaXN0YW5jZSA9IGFzLm51bWVyaWMoZXVjbF9kaXN0YW5jZXNfYWxsX05TQ3NbIGNlbGxzMmNvbXBhcmVfeW91bmcgLCB4IF0pICwgY29tcGFyaXNvbiA9IGFzLmNoYXJhY3RlcigieW91bmdfdG9feW91bmciKSAsIFBzZXVkb3RpbWUgPSBwc2V1ZG90aW1lX3ZhbCApIAogICAgCiAgICB5b3VuZ190b19vbGQgPC0gZGF0YS5mcmFtZSggZXVjbGlkZWFuX2Rpc3RhbmNlID0gYXMubnVtZXJpYyhldWNsX2Rpc3RhbmNlc19hbGxfTlNDc1sgY2VsbHMyY29tcGFyZV9vbGQgLCB4IF0pICwgY29tcGFyaXNvbiA9IGFzLmNoYXJhY3RlcigieW91bmdfdG9fb2xkIikgLCBQc2V1ZG90aW1lID0gcHNldWRvdGltZV92YWwgKQogICAgCiAgICB5b3VuZ190b195b3VuZyRjb21wYXJpc29uIDwtIGFzLmNoYXJhY3Rlcih5b3VuZ190b195b3VuZyRjb21wYXJpc29uKQogICAgeW91bmdfdG9fb2xkJGNvbXBhcmlzb24gPC0gYXMuY2hhcmFjdGVyKHlvdW5nX3RvX29sZCRjb21wYXJpc29uKQogICAgCiAgICBkYXRhX2NvbXBhcmlzb25zIDwtIGJpbmRfcm93cyggeW91bmdfdG9feW91bmcgLCB5b3VuZ190b19vbGQgKQogICAgCiAgfWVsc2V7CiAgICBkYXRhX2NvbXBhcmlzb25zIDwtIGRhdGEuZnJhbWUoIGV1Y2xpZGVhbl9kaXN0YW5jZSA9IGFzLm51bWVyaWMoZXVjbF9kaXN0YW5jZXNfYWxsX05TQ3NbIGNlbGxzMmNvbXBhcmVfb2xkICwgeCBdKSAsIGNvbXBhcmlzb24gPSBhcy5jaGFyYWN0ZXIoIm9sZF90b19vbGQiKSAsIFBzZXVkb3RpbWUgPSBwc2V1ZG90aW1lX3ZhbCApCiAgICAKICAgIGRhdGFfY29tcGFyaXNvbnMkY29tcGFyaXNvbiA8LSBhcy5jaGFyYWN0ZXIoZGF0YV9jb21wYXJpc29ucyRjb21wYXJpc29uKQogICAgCiAgfQogICAgCiAgZGF0YV9jb21wYXJpc29ucwp9KQpgYGAKCmBgYHtyfQpkYXRhX2V1Y2xpZGVhbl9kaXN0YW5jZXNfZGYgPC0gYmluZF9yb3dzKCBkYXRhX2V1Y2xpZGVhbl9kaXN0YW5jZXMgKQpgYGAKCmBgYHtyfQpxTlNDMV9jZWxscyA8LSBXaGljaENlbGxzKCBvYmplY3QgPSBzZXVyYXRfMTBYMiAsIGlkZW50ID0gInFOU0MxIikKcU5TQzJfY2VsbHMgPC0gV2hpY2hDZWxscyggb2JqZWN0ID0gc2V1cmF0XzEwWDIgLCBpZGVudCA9ICJxTlNDMiIpCmBgYAoKCmBgYHtyfQpwc2V1ZG90aW1lX2RpZmYgPSAyLjUKCmRhdGFfZXVjbGlkZWFuX2Rpc3RhbmNlc19xMV9xMl9wc2V1ZG90aW1ld2luZG93IDwtIGxhcHBseSggWCA9IGMocU5TQzFfY2VsbHMpICAsIEZVTiA9IGZ1bmN0aW9uKHgpewogICAgcmVxdWlyZShkcGx5cikKICAKICAgIHBzZXVkb3RpbWVfdmFsIDwtIG1ldGFfZGF0YV90YWJsZSAlPiUgZmlsdGVyKCBjZWxsID09IHggICkgJT4lIHB1bGwoIlBzZXVkb3RpbWUiKQogIAogICAgdHlwZV9jZWxsIDwtIG1ldGFfZGF0YV90YWJsZSAlPiUgZmlsdGVyKCBjZWxsID09IHggICkgJT4lIHB1bGwoImNlbGx0eXBlIikKICAKICAgIHBzZXVkb3RpbWVfdmFscyA8LSBjKCBtaW4gPSBwc2V1ZG90aW1lX3ZhbCAtIHBzZXVkb3RpbWVfZGlmZiAsIHZhbHVlID0gcHNldWRvdGltZV92YWwgICwgbWF4ID0gcHNldWRvdGltZV92YWwgKyBwc2V1ZG90aW1lX2RpZmYgKSAKICAgIAogICAgY2VsbHMyY29tcGFyZSA8LSBtZXRhX2RhdGFfdGFibGUgJT4lIGZpbHRlciggUHNldWRvdGltZSA8IHBzZXVkb3RpbWVfdmFsc1sibWF4Il0gICYgIFBzZXVkb3RpbWUgPiBwc2V1ZG90aW1lX3ZhbHNbIm1pbiJdICkgCiAgICAKICAgIGNlbGxzMmNvbXBhcmVfcTEgPC0gY2VsbHMyY29tcGFyZSAlPiUgZmlsdGVyKCBjZWxsdHlwZSA9PSAicU5TQzEiICYgY2VsbCAhPSB4ICkgJT4lIHB1bGwoImNlbGwiKSAKCiAgICBjZWxsczJjb21wYXJlX3EyIDwtIGNlbGxzMmNvbXBhcmUgJT4lIGZpbHRlciggY2VsbHR5cGUgPT0gInFOU0MyIiAmIGNlbGwgIT0geCApICU+JSBwdWxsKCJjZWxsIikKICAgICAgICAKICBpZih0eXBlX2NlbGwgPT0gInFOU0MxIil7CiAgIAogICAgcTFfdG9fcTIgPC0gZGF0YS5mcmFtZSggZXVjbGlkZWFuX2Rpc3RhbmNlID0gYXMubnVtZXJpYyhldWNsX2Rpc3RhbmNlc19hbGxfTlNDc1sgY2VsbHMyY29tcGFyZV9xMiAsIHggXSkgLCBjb21wYXJpc29uID0gYXMuY2hhcmFjdGVyKCJxTlNDMV92c19xTlNDMiIpICwgUHNldWRvdGltZSA9IHBzZXVkb3RpbWVfdmFsICkKICAgIAogICAgcTFfdG9fcTIkY29tcGFyaXNvbiA8LSBhcy5jaGFyYWN0ZXIocTFfdG9fcTIkY29tcGFyaXNvbikKICAgIAogICAgZGF0YV9jb21wYXJpc29ucyA8LSBxMV90b19xMgogIH1lbHNlewogICAgc3RvcCgiRXJyb3IiKQogIH0KICAgIAogIGRhdGFfY29tcGFyaXNvbnMKfSkKYGBgCgpgYGB7cn0KZGF0YV9ldWNsaWRlYW5fZGlzdGFuY2VzX2RmX3ExX3EyIDwtIGJpbmRfcm93cyggZGF0YV9ldWNsaWRlYW5fZGlzdGFuY2VzX3ExX3EyX3BzZXVkb3RpbWV3aW5kb3cgKQpgYGAKCiMjIE1lcmdlIHRoZSBhZ2UgY29tcGFyaXNvbnMgd2l0aCB0aGUgYWxsIHZzIGFsbCBjb21wYXJpc29uCgpgYGB7cn0KZGF0YV9ldWNsaWRlYW5fZGlzdGFuY2VzX2NvbWJpbmVkX2RmIDwtIGJpbmRfcm93cyggZGF0YV9ldWNsaWRlYW5fZGlzdGFuY2VzX2RmICwgZGF0YV9ldWNsaWRlYW5fZGlzdGFuY2VzX2RmX3ExX3EyICApCgpkYXRhX2V1Y2xpZGVhbl9kaXN0YW5jZXNfY29tYmluZWRfZGYgPC0gIGRhdGFfZXVjbGlkZWFuX2Rpc3RhbmNlc19jb21iaW5lZF9kZiAlPiUgZ3JvdXBfYnkoY29tcGFyaXNvbikgJT4lIG11dGF0ZSggbWVhbl9ldWNsaWRlYW5fZGlzdGFuY2UgPSBtZWFuKGV1Y2xpZGVhbl9kaXN0YW5jZSkgKQpgYGAKCgojIyBEZW5zaXR5IG9mIGV1Y2xpZGVhbiBkaXN0YW5jZXMKCmBgYHtyfQpjb21wYXJpc29uX2NvbG9ycyA8LSBjKCJvbGRfdG9fb2xkIiA9ICJzbGF0ZWJsdWUiICwieW91bmdfdG9fb2xkIiA9ICJkZWVwc2t5Ymx1ZTQiLCJ5b3VuZ190b195b3VuZyIgPSAib2xpdmVkcmFiIiwicU5TQzFfdnNfcU5TQzIiID0gImJsYWNrIikKYGBgCgpgYGB7cn0KZGF0YV9ldWNsaWRlYW5fZGlzdGFuY2VzX2NvbWJpbmVkX2RmJGNvbXBhcmlzb24gPC0gZmFjdG9yKCBkYXRhX2V1Y2xpZGVhbl9kaXN0YW5jZXNfY29tYmluZWRfZGYkY29tcGFyaXNvbiAsIGxldmVscyA9IGMoInFOU0MxX3ZzX3FOU0MyIiAsICJ5b3VuZ190b195b3VuZyIgLCAieW91bmdfdG9fb2xkIiAsICAib2xkX3RvX29sZCIgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9Nn0KZ2dfcHNldWRvdGltZXdpbmRvdyA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGFfZXVjbGlkZWFuX2Rpc3RhbmNlc19jb21iaW5lZF9kZiAsIG1hcHBpbmcgPSBhZXMoeCA9IGV1Y2xpZGVhbl9kaXN0YW5jZSAsIGNvbG9yID0gY29tcGFyaXNvbiAsIGZpbGwgPSBjb21wYXJpc29uICkpICsgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KSArICBnZ3RpdGxlKCAiRXVjbGlkZWFuIGRpc3RhbmNlcyIgKSAgKyBnZW9tX3ZsaW5lKG1hcHBpbmcgPSBhZXMoeGludGVyY2VwdCA9IG1lYW5fZXVjbGlkZWFuX2Rpc3RhbmNlICkgLCBjb2xvciA9ICJncmV5MjAiICkgKyBmYWNldF9ncmlkKGZhY2V0cyA9IGNvbXBhcmlzb24gfiAuKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9ICBjb21wYXJpc29uX2NvbG9ycyAsIGxhYmVscyA9IGMoInFOU0MxIHRvIHFOU0MyIiAsICJ5b3VuZyB0byB5b3VuZyIgLCAieW91bmcgdG8gb2xkIiAsICAib2xkIHRvIG9sZCIgKSAsIG5hbWUgPSAiQ29tcGFyaXNvbiIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9ICBjb21wYXJpc29uX2NvbG9ycyAsIGxhYmVscyA9IGMoInFOU0MxIHRvIHFOU0MyIiAsICJ5b3VuZyB0byB5b3VuZyIgLCAieW91bmcgdG8gb2xkIiAsICAib2xkIHRvIG9sZCIgKSAsIG5hbWUgPSAiQ29tcGFyaXNvbiIgKSArIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCgpnZ19wc2V1ZG90aW1ld2luZG93CmBgYAoKIyBFbmQKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=